diff --git a/.gitignore b/.gitignore index a05a18f..cc5bee7 100644 --- a/.gitignore +++ b/.gitignore @@ -221,4 +221,5 @@ index.d.ts.map /*.session.sql /temp -/rustc-ice-* \ No newline at end of file +/rustc-ice-* +/data \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..427cd52 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,88 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "lldb", + "request": "launch", + "name": "debug quirks_path lib", + "cargo": { + "args": [ + "test", + "--no-run", + "--lib", + "--package=quirks_path" + ], + "filter": { + "name": "quirks_path", + "kind": "lib" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "debug recorder bin", + "cargo": { + "args": [ + "build", + "--bin=recorder_cli", + "--package=recorder", + ], + "filter": { + "name": "recorder_cli", + "kind": "bin" + } + }, + "args": [ + "--environment", + "recorder.development" + ], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "debug playground example", + "cargo": { + "args": [ + "build", + "--example=playground", + "--package=recorder", + ], + "filter": { + "name": "playground", + "kind": "example" + } + }, + "args": [ + "--environment", + "recorder.development" + ], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "debug record lib", + "cargo": { + "args": [ + "test", + "--no-run", + "--test=mod", + "--package=recorder" + ], + "filter": { + "name": "mod", + "kind": "test" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + } + ] +} \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 3b374cf..b5e9c36 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,32 +1,21 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" -version = "0.21.0" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] [[package]] -name = "adler" -version = "1.0.2" +name = "adler2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "aes" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures", -] +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "ahash" @@ -41,9 +30,9 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.9" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d713b3834d76b85304d4d525563c1276e2e30dc97cc67bfb4585a4a29fc2c89f" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", "getrandom", @@ -54,9 +43,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] @@ -84,9 +73,9 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.16" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "android-tzdata" @@ -105,57 +94,58 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.12" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b09b5178381e0874812a9b157f7fe84982617e48f71f4e3235482775e5b540" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", + "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.6" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" -version = "0.2.3" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.2" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.2" +version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "anyhow" -version = "1.0.80" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1" +checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" [[package]] name = "argon2" @@ -171,15 +161,25 @@ dependencies = [ [[package]] name = "arrayvec" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "assert-json-diff" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e4f2b81832e72834d7518d8487a0396a28cc408186a2e8854c0f98011faf12" +dependencies = [ + "serde", + "serde_json", +] [[package]] name = "async-compression" -version = "0.4.6" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a116f46a969224200a0a97f29cfd4c50e7534e4b4826bd23ea2c3c533039c82c" +checksum = "df895a515f70646414f4b45c0b79082783b80552b373a68283012928df56f522" dependencies = [ "brotli", "flate2", @@ -193,9 +193,9 @@ dependencies = [ [[package]] name = "async-stream" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" dependencies = [ "async-stream-impl", "futures-core", @@ -204,24 +204,24 @@ dependencies = [ [[package]] name = "async-stream-impl" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.91", ] [[package]] name = "async-trait" -version = "0.1.77" +version = "0.1.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" +checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.91", ] [[package]] @@ -235,26 +235,22 @@ dependencies = [ [[package]] name = "atom_syndication" -version = "0.12.2" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "571832dcff775e26562e8e6930cd483de5587301d40d3a3b85d532b6383e15a7" +checksum = "ec03a6e158ee0f38bfba811976ae909bc2505a4a2f4049c7e8df47df3497b119" dependencies = [ "chrono", "derive_builder", "diligent-date-parser", "never", - "quick-xml 0.30.0", + "quick-xml 0.37.1", ] [[package]] -name = "atomic-write-file" -version = "0.1.2" +name = "atomic-waker" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edcdbedc2236483ab103a53415653d6b4442ea6141baf1ffa85df29635e88436" -dependencies = [ - "nix", - "rand", -] +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "auto-future" @@ -264,28 +260,28 @@ checksum = "3c1e7e457ea78e524f48639f551fd79703ac3f2237f5ecccdf4708f8a75ad373" [[package]] name = "autocfg" -version = "1.1.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "axum" -version = "0.7.4" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1236b4b292f6c4d6dc34604bb5120d85c3fe1d1aa596bd5cc52ca054d13e7b9e" +checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" dependencies = [ "async-trait", "axum-core", "axum-macros", "bytes", "futures-util", - "http 1.0.0", - "http-body 1.0.0", + "http 1.2.0", + "http-body", "http-body-util", - "hyper 1.2.0", + "hyper", "hyper-util", "itoa", - "matchit", + "matchit 0.7.3", "memchr", "mime", "percent-encoding", @@ -297,7 +293,7 @@ dependencies = [ "serde_urlencoded", "sync_wrapper", "tokio", - "tower", + "tower 0.5.2", "tower-layer", "tower-service", "tracing", @@ -305,15 +301,15 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" +checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" dependencies = [ "async-trait", "bytes", "futures-util", - "http 1.0.0", - "http-body 1.0.0", + "http 1.2.0", + "http-body", "http-body-util", "mime", "pin-project-lite", @@ -326,53 +322,55 @@ dependencies = [ [[package]] name = "axum-extra" -version = "0.9.2" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "895ff42f72016617773af68fb90da2a9677d89c62338ec09162d4909d86fdd8f" +checksum = "c794b30c904f0a1c2fb7740f7df7f7972dfaa14ef6f57cb6178dc63e5dca2f04" dependencies = [ "axum", "axum-core", "bytes", "cookie", + "fastrand", "futures-util", - "http 1.0.0", - "http-body 1.0.0", + "http 1.2.0", + "http-body", "http-body-util", "mime", + "multer", "pin-project-lite", "serde", - "tower", + "tower 0.5.2", "tower-layer", "tower-service", ] [[package]] name = "axum-macros" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00c055ee2d014ae5981ce1016374e8213682aa14d9bf40e48ab48b5f3ef20eaa" +checksum = "57d123550fa8d071b7255cb0cc04dc302baa6c8c4a79f55701552684d8399bce" dependencies = [ - "heck", "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.91", ] [[package]] name = "axum-test" -version = "14.3.0" +version = "16.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc431b62ab307c833af24700936485eb5f9a8ac18a19347fe37dd4f7ae3dffe9" +checksum = "63e3a443d2608936a02a222da7b746eb412fede7225b3030b64fe9be99eab8dc" dependencies = [ "anyhow", - "async-trait", + "assert-json-diff", "auto-future", "axum", "bytes", + "bytesize", "cookie", - "http 1.0.0", + "http 1.2.0", "http-body-util", - "hyper 1.2.0", + "hyper", "hyper-util", "mime", "pretty_assertions", @@ -383,35 +381,34 @@ dependencies = [ "serde_urlencoded", "smallvec", "tokio", - "tower", + "tower 0.5.2", "url", ] [[package]] name = "backon" -version = "0.4.2" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c79c8ef183b8b663e8cb19cf92fb7d98c56739977bd47eae2de2717bd5de2c2c" +checksum = "ba5289ec98f68f28dd809fd601059e6aa908bb8f6108620930828283d4ee23d7" dependencies = [ "fastrand", - "futures-core", - "pin-project", + "gloo-timers", "tokio", ] [[package]] name = "backtrace" -version = "0.3.69" +version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ "addr2line", - "cc", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", + "windows-targets 0.52.6", ] [[package]] @@ -423,21 +420,21 @@ dependencies = [ "btparse-stable", "colored", "regex", - "thiserror", + "thiserror 1.0.69", ] -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - [[package]] name = "base64" version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "base64-simd" version = "0.7.0" @@ -455,42 +452,44 @@ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "bb8" -version = "0.8.3" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df7c2093d15d6a1d33b1f972e1c5ea3177748742b97a5f392aa83a65262c6780" +checksum = "d89aabfae550a5c44b43ab941844ffcd2e993cb6900b342debf59e9ea74acdb8" dependencies = [ "async-trait", - "futures-channel", "futures-util", - "parking_lot", + "parking_lot 0.12.3", "tokio", ] [[package]] name = "bigdecimal" -version = "0.3.1" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6773ddc0eafc0e509fb60e48dff7f450f8e674a0686ae8605e8d9901bd5eefa" +checksum = "7f31f3af01c5c65a07985c804d3366560e6fa7883d640a122819b14ec327482c" dependencies = [ + "autocfg", + "libm", "num-bigint", "num-integer", "num-traits", + "serde", ] [[package]] name = "bit-set" -version = "0.5.3" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" dependencies = [ "bit-vec", ] [[package]] name = "bit-vec" -version = "0.6.3" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" [[package]] name = "bitflags" @@ -500,9 +499,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.2" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" dependencies = [ "serde", ] @@ -538,29 +537,60 @@ dependencies = [ ] [[package]] -name = "block-padding" -version = "0.3.3" +name = "bollard" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" +checksum = "d41711ad46fda47cd701f6908e59d1bd6b9a2b7464c0d0aeab95c6d37096ff8a" dependencies = [ - "generic-array", + "base64 0.22.1", + "bollard-stubs", + "bytes", + "futures-core", + "futures-util", + "hex 0.4.3", + "home", + "http 1.2.0", + "http-body-util", + "hyper", + "hyper-named-pipe", + "hyper-rustls", + "hyper-util", + "hyperlocal", + "log", + "pin-project-lite", + "rustls", + "rustls-native-certs", + "rustls-pemfile", + "rustls-pki-types", + "serde", + "serde_derive", + "serde_json", + "serde_repr", + "serde_urlencoded", + "thiserror 1.0.69", + "tokio", + "tokio-util", + "tower-service", + "url", + "winapi", ] [[package]] name = "bollard-stubs" -version = "1.42.0-rc.3" +version = "1.45.0-rc.26.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed59b5c00048f48d7af971b71f800fdf23e858844a6f9e4d32ca72e9399e7864" +checksum = "6d7c5415e3a6bc6d3e99eff6268e488fd4ee25e7b28c10f08fa6760bd9de16e4" dependencies = [ "serde", - "serde_with 1.14.0", + "serde_repr", + "serde_with", ] [[package]] name = "borsh" -version = "1.3.1" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f58b559fd6448c6e2fd0adb5720cd98a2506594cafa4737ff98c396f3e82f667" +checksum = "2506947f73ad44e344215ccd6403ac2ae18cd8e046e581a441bf8d199f257f03" dependencies = [ "borsh-derive", "cfg_aliases", @@ -568,23 +598,22 @@ dependencies = [ [[package]] name = "borsh-derive" -version = "1.3.1" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aadb5b6ccbd078890f6d7003694e33816e6b784358f18e15e7e6d9f065a57cd" +checksum = "c2593a3b8b938bd68373196c9832f516be11fa487ef4ae745eb282e6a56a7244" dependencies = [ "once_cell", "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.50", - "syn_derive", + "syn 2.0.91", ] [[package]] name = "brotli" -version = "3.4.0" +version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "516074a47ef4bce09577a3b379392300159ce5b1ba2e501ff1c819950066100f" +checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -593,9 +622,9 @@ dependencies = [ [[package]] name = "brotli-decompressor" -version = "2.5.1" +version = "4.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f" +checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -603,9 +632,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.9.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc" +checksum = "786a307d683a5bf92e6fd5fd69a7eb613751668d1d8d67d802846dfe367c62c8" dependencies = [ "memchr", "serde", @@ -619,9 +648,9 @@ checksum = "0d75b8252ed252f881d1dc4482ae3c3854df6ee8183c1906bac50ff358f4f89f" [[package]] name = "bumpalo" -version = "3.15.3" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea184aa71bb362a1157c896979544cc23974e08fd265f29ea96b59f0b4a555b" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "byte-unit" @@ -655,6 +684,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "bytemuck" +version = "1.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3" + [[package]] name = "byteorder" version = "1.5.0" @@ -663,58 +698,25 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.5.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" [[package]] -name = "camino" -version = "1.1.6" +name = "bytesize" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" -dependencies = [ - "serde", -] - -[[package]] -name = "cargo-platform" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "694c8807f2ae16faecc43dc17d74b3eb042482789fd0eb64b39a2e04e087053f" -dependencies = [ - "serde", -] - -[[package]] -name = "cargo_metadata" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" -dependencies = [ - "camino", - "cargo-platform", - "semver", - "serde", - "serde_json", - "thiserror", -] - -[[package]] -name = "cbc" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" -dependencies = [ - "cipher", -] +checksum = "a3e368af43e418a04d52505cf3dbc23dda4e3407ae2fa99fd0e4f308ce546acc" [[package]] name = "cc" -version = "1.0.86" +version = "1.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9fa1897e4325be0d68d48df6aa1a71ac2ed4d27723887e7754192705350730" +checksum = "c31a0499c1dc64f458ad13872de75c0eb7e3fdb0e67964610c914b034fc5956e" dependencies = [ + "jobserver", "libc", + "shlex", ] [[package]] @@ -725,15 +727,15 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cfg_aliases" -version = "0.1.1" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" -version = "0.4.34" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" dependencies = [ "android-tzdata", "iana-time-zone", @@ -741,29 +743,29 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-targets 0.52.3", + "windows-targets 0.52.6", ] [[package]] name = "chrono-tz" -version = "0.8.6" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59ae0466b83e838b81a54256c39d5d7c20b9d7daa10510a242d9b75abd5936e" +checksum = "93698b29de5e97ad0ae26447b344c482a7284c737d9ddc5f9e52b74a336671bb" dependencies = [ "chrono", "chrono-tz-build", - "phf 0.11.2", + "phf", ] [[package]] name = "chrono-tz-build" -version = "0.2.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433e39f13c9a060046954e0592a8d0a4bcb1040125cbf91cb8ee58964cfb350f" +checksum = "0c088aee841df9c3041febbb73934cfc39708749bf96dc827e3359cd39ef11b1" dependencies = [ "parse-zoneinfo", - "phf 0.11.2", - "phf_codegen 0.11.2", + "phf", + "phf_codegen", ] [[package]] @@ -772,25 +774,15 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8eebd66744a15ded14960ab4ccdbfb51ad3b81f51f3f04a80adac98c985396c9" dependencies = [ - "hashbrown 0.14.3", + "hashbrown 0.14.5", "stacker", ] -[[package]] -name = "cipher" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" -dependencies = [ - "crypto-common", - "inout", -] - [[package]] name = "clap" -version = "4.5.1" +version = "4.5.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da" +checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84" dependencies = [ "clap_builder", "clap_derive", @@ -798,55 +790,55 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.1" +version = "4.5.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb" +checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838" dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim 0.11.0", + "strsim", ] [[package]] name = "clap_derive" -version = "4.5.0" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.91", ] [[package]] name = "clap_lex" -version = "0.7.0" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "colorchoice" -version = "1.0.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "colored" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" +checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" dependencies = [ "lazy_static", - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] name = "combine" -version = "4.6.6" +version = "4.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" dependencies = [ "bytes", "futures-core", @@ -875,15 +867,25 @@ dependencies = [ ] [[package]] -name = "console" -version = "0.15.8" +name = "concurrent-queue" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "console" +version = "0.15.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea3c6ecd8059b57859df5c69830340ed3c41d30e3da0c1cbed90a96ac853041b" dependencies = [ "encode_unicode", - "lazy_static", "libc", - "windows-sys 0.52.0", + "once_cell", + "unicode-width 0.2.0", + "windows-sys 0.59.0", ] [[package]] @@ -892,26 +894,6 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" -[[package]] -name = "const-random" -version = "0.1.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aaf16c9c2c612020bcfd042e170f6e32de9b9d75adb5277cdbbd2e2c8c8299a" -dependencies = [ - "const-random-macro", -] - -[[package]] -name = "const-random-macro" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" -dependencies = [ - "getrandom", - "once_cell", - "tiny-keccak", -] - [[package]] name = "const-str" version = "0.3.2" @@ -933,10 +915,19 @@ dependencies = [ ] [[package]] -name = "cookie" -version = "0.18.0" +name = "convert_case" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cd91cf61412820176e137621345ee43b3f4423e589e7ae4e50d601d93e35ef8" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "cookie" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" dependencies = [ "percent-encoding", "time", @@ -955,24 +946,24 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.12" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" dependencies = [ "libc", ] [[package]] name = "crc" -version = "3.0.1" +version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" +checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" dependencies = [ "crc-catalog", ] @@ -985,13 +976,24 @@ checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" [[package]] name = "crc32fast" -version = "1.4.0" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if", ] +[[package]] +name = "cron" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f8c3e73077b4b4a6ab1ea5047c37c57aee77657bc8ecd6f29b0af082d0b0c07" +dependencies = [ + "chrono", + "nom", + "once_cell", +] + [[package]] name = "cron_clock" version = "0.8.0" @@ -1004,10 +1006,19 @@ dependencies = [ ] [[package]] -name = "crossbeam-deque" -version = "0.8.5" +name = "crossbeam-channel" +version = "0.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -1024,43 +1035,18 @@ dependencies = [ [[package]] name = "crossbeam-queue" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.19" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" - -[[package]] -name = "crossterm" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67" -dependencies = [ - "bitflags 1.3.2", - "crossterm_winapi", - "libc", - "mio", - "parking_lot", - "signal-hook", - "signal-hook-mio", - "winapi", -] - -[[package]] -name = "crossterm_winapi" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" -dependencies = [ - "winapi", -] +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "cruet" @@ -1072,12 +1058,6 @@ dependencies = [ "regex", ] -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "crypto-common" version = "0.1.6" @@ -1109,7 +1089,20 @@ dependencies = [ "cssparser-macros", "dtoa-short", "itoa", - "phf 0.11.2", + "phf", + "smallvec", +] + +[[package]] +name = "cssparser" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c66d1cd8ed61bf80b38432613a7a2f09401ab8d0501110655f8b341484a3e3" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa", + "phf", "smallvec", ] @@ -1119,7 +1112,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "556c099a61d85989d7af52b692e35a8d68a57e7df8c6d07563dc0778b3960c9f" dependencies = [ - "cssparser", + "cssparser 0.33.0", ] [[package]] @@ -1129,112 +1122,42 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" dependencies = [ "quote", - "syn 2.0.50", + "syn 2.0.91", ] [[package]] name = "darling" -version = "0.13.4" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" dependencies = [ - "darling_core 0.13.4", - "darling_macro 0.13.4", -] - -[[package]] -name = "darling" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" -dependencies = [ - "darling_core 0.14.4", - "darling_macro 0.14.4", -] - -[[package]] -name = "darling" -version = "0.20.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391" -dependencies = [ - "darling_core 0.20.8", - "darling_macro 0.20.8", + "darling_core", + "darling_macro", ] [[package]] name = "darling_core" -version = "0.13.4" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", - "strsim 0.10.0", - "syn 1.0.109", -] - -[[package]] -name = "darling_core" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim 0.10.0", - "syn 1.0.109", -] - -[[package]] -name = "darling_core" -version = "0.20.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim 0.10.0", - "syn 2.0.50", + "strsim", + "syn 2.0.91", ] [[package]] name = "darling_macro" -version = "0.13.4" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ - "darling_core 0.13.4", + "darling_core", "quote", - "syn 1.0.109", -] - -[[package]] -name = "darling_macro" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" -dependencies = [ - "darling_core 0.14.4", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "darling_macro" -version = "0.20.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" -dependencies = [ - "darling_core 0.20.8", - "quote", - "syn 2.0.50", + "syn 2.0.91", ] [[package]] @@ -1244,17 +1167,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ "cfg-if", - "hashbrown 0.14.3", + "hashbrown 0.14.5", "lock_api", "once_cell", - "parking_lot_core", + "parking_lot_core 0.9.10", ] [[package]] name = "data-encoding" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" [[package]] name = "data-url" @@ -1267,9 +1190,9 @@ dependencies = [ [[package]] name = "der" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" dependencies = [ "const-oid", "pem-rfc7468", @@ -1286,53 +1209,66 @@ dependencies = [ "serde", ] -[[package]] -name = "derivative" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "derive_builder" -version = "0.12.0" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d67778784b508018359cbc8696edb3db78160bab2c2a28ba7f56ef6932997f8" +checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" dependencies = [ "derive_builder_macro", ] [[package]] name = "derive_builder_core" -version = "0.12.0" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c11bdc11a0c47bc7d37d582b5285da6849c96681023680b906673c5707af7b0f" +checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" dependencies = [ - "darling 0.14.4", + "darling", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.91", ] [[package]] name = "derive_builder_macro" -version = "0.12.0" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebcda35c7a396850a55ffeac740804b40ffec779b98fffbb1738f4033f0ee79e" +checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 1.0.109", + "syn 2.0.91", +] + +[[package]] +name = "derive_more" +version = "0.99.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", ] [[package]] name = "deunicode" -version = "1.4.3" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6e854126756c496b8c81dec88f9a706b15b875c5849d4097a3854476b9fdf94" +checksum = "339544cc9e2c4dc3fc7149fd630c5f22263a4fdf18a98afd0075784968b5cf00" + +[[package]] +name = "dialoguer" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658bce805d770f407bc62102fca7c2c64ceef2fbcb2b8bd19d2765ce093980de" +dependencies = [ + "console", + "shell-words", + "tempfile", + "thiserror 1.0.69", + "zeroize", +] [[package]] name = "diff" @@ -1354,9 +1290,9 @@ dependencies = [ [[package]] name = "diligent-date-parser" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6cf7fe294274a222363f84bcb63cdea762979a0443b4cf1f4f8fd17c86b1182" +checksum = "c8ede7d79366f419921e2e2f67889c12125726692a313bffb474bd5f37a581e9" dependencies = [ "chrono", ] @@ -1404,19 +1340,26 @@ dependencies = [ ] [[package]] -name = "dlv-list" -version = "0.5.2" +name = "displaydoc" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ - "const-random", + "proc-macro2", + "quote", + "syn 2.0.91", ] [[package]] -name = "doc-comment" -version = "0.3.3" +name = "docker_credential" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" +checksum = "31951f49556e34d90ed28342e1df7e1cb7a229c4cab0aecc627b5d91edd41d07" +dependencies = [ + "base64 0.21.7", + "serde", + "serde_json", +] [[package]] name = "dotenvy" @@ -1432,9 +1375,9 @@ checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" [[package]] name = "dtoa-short" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbaceec3c6e4211c79e7b1800fb9680527106beb2f9c51904a3210c03a448c74" +checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87" dependencies = [ "dtoa", ] @@ -1452,45 +1395,70 @@ dependencies = [ ] [[package]] -name = "either" -version = "1.10.0" +name = "duct_sh" +version = "0.13.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" +checksum = "5a6633cadba557545fbbe0299a2f9adc4bb2fc5fb238773f5e841e0c23d62146" +dependencies = [ + "duct", +] + +[[package]] +name = "ego-tree" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2972feb8dffe7bc8c5463b1dacda1b0dfbed3710e50f977d965429692d74cd8" + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" dependencies = [ "serde", ] [[package]] name = "email-encoding" -version = "0.2.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbfb21b9878cf7a348dcb8559109aabc0ec40d69924bd706fa5149846c4fef75" +checksum = "ea3d894bbbab314476b265f9b2d46bf24b123a36dd0e96b06a1b49545b9d9dcc" dependencies = [ - "base64 0.21.7", + "base64 0.22.1", "memchr", ] [[package]] name = "email_address" -version = "0.2.4" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2153bd83ebc09db15bcbdc3e2194d901804952e3dc96967e1cd3b0c5c32d112" +checksum = "e079f19b08ca6239f47f8ba8509c11cf3ea30095831f7fed61441475edd8c449" [[package]] name = "encode_unicode" -version = "0.3.6" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" [[package]] name = "encoding_rs" -version = "0.8.33" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" dependencies = [ "cfg-if", ] +[[package]] +name = "english-to-cron" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c16423ac933fee80f05a52b435a912d5b08edbbbfe936e0042ebb3accdf303da" +dependencies = [ + "lazy_static", + "regex", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -1499,12 +1467,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.8" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1520,9 +1488,14 @@ dependencies = [ [[package]] name = "event-listener" -version = "2.5.3" +version = "5.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] [[package]] name = "eyre" @@ -1536,38 +1509,38 @@ dependencies = [ [[package]] name = "fancy-regex" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "531e46835a22af56d1e3b66f04844bed63158bc094a628bec1d321d9b4c44bf2" +checksum = "6e24cb5a94bcae1e5408b0effca5cd7172ea3c5755049c5f3af4cd283a165298" dependencies = [ "bit-set", - "regex-automata 0.4.5", - "regex-syntax 0.8.2", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", ] [[package]] name = "fastrand" -version = "2.0.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] -name = "finl_unicode" -version = "1.2.0" +name = "filetime" +version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fcfdc7a0362c9f4444381a9e697c79d435fe65b52a37466fc2c1184cee9edc6" - -[[package]] -name = "flagset" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a7e408202050813e6f1d9addadcaafef3dca7530c7ddfb005d4081cce6779" +checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" +dependencies = [ + "cfg-if", + "libc", + "libredox", + "windows-sys 0.59.0", +] [[package]] name = "flate2" -version = "1.0.28" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" dependencies = [ "crc32fast", "miniz_oxide", @@ -1575,13 +1548,13 @@ dependencies = [ [[package]] name = "flume" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" +checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" dependencies = [ "futures-core", "futures-sink", - "spin 0.9.8", + "spin", ] [[package]] @@ -1630,10 +1603,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] -name = "futures" -version = "0.3.30" +name = "futf" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" +dependencies = [ + "mac", + "new_debug_unreachable", +] + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", @@ -1646,9 +1629,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", @@ -1656,15 +1639,15 @@ 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", @@ -1679,37 +1662,37 @@ checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" dependencies = [ "futures-core", "lock_api", - "parking_lot", + "parking_lot 0.12.3", ] [[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.50", + "syn 2.0.91", ] [[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-timer" @@ -1719,9 +1702,9 @@ checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" [[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", @@ -1765,10 +1748,19 @@ dependencies = [ ] [[package]] -name = "getrandom" -version = "0.2.12" +name = "getopts" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" +dependencies = [ + "unicode-width 0.1.14", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "js-sys", @@ -1779,9 +1771,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.1" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "glob" @@ -1791,60 +1783,53 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "globset" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" +checksum = "15f1ce686646e7f1e19bf7d5533fe443a45dbfb990e00629110797578b42fb19" dependencies = [ "aho-corasick", "bstr", "log", - "regex-automata 0.4.5", - "regex-syntax 0.8.2", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", ] [[package]] name = "globwalk" -version = "0.8.1" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc" +checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", "ignore", "walkdir", ] [[package]] -name = "h2" -version = "0.3.24" +name = "gloo-timers" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" +checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" dependencies = [ - "bytes", - "fnv", + "futures-channel", "futures-core", - "futures-sink", - "futures-util", - "http 0.2.11", - "indexmap 2.2.3", - "slab", - "tokio", - "tokio-util", - "tracing", + "js-sys", + "wasm-bindgen", ] [[package]] name = "h2" -version = "0.4.2" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31d030e59af851932b72ceebadf4a2b5986dba4c3b99dd2493f8273a0f151943" +checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" dependencies = [ + "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "futures-util", - "http 1.0.0", - "indexmap 2.2.3", + "http 1.2.0", + "indexmap 2.7.0", "slab", "tokio", "tokio-util", @@ -1862,21 +1847,27 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ - "ahash 0.8.9", + "ahash 0.8.11", "allocator-api2", ] [[package]] -name = "hashlink" -version = "0.8.4" +name = "hashbrown" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" + +[[package]] +name = "hashlink" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" dependencies = [ - "hashbrown 0.14.3", + "hashbrown 0.14.5", ] [[package]] @@ -1884,15 +1875,24 @@ name = "heck" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" -dependencies = [ - "unicode-segmentation", -] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.3.6" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd5256b483761cd23699d0da46cc6fd2ee3be420bbe6d020ae4a091e70b7e9fd" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" [[package]] name = "hex" @@ -1926,22 +1926,22 @@ dependencies = [ [[package]] name = "home" -version = "0.5.9" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "hostname" -version = "0.3.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +checksum = "f9c7c7c8ac16c798734b8a24560c1362120597c40d5e1459f09498f8f6c8f2ba" dependencies = [ + "cfg-if", "libc", - "match_cfg", - "winapi", + "windows", ] [[package]] @@ -1954,10 +1954,24 @@ dependencies = [ ] [[package]] -name = "http" -version = "0.2.11" +name = "html5ever" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +checksum = "2e15626aaf9c351bc696217cbe29cb9b5e86c43f8a46b5e2f5c6c5cf7cb904ce" +dependencies = [ + "log", + "mac", + "markup5ever", + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ "bytes", "fnv", @@ -1966,9 +1980,9 @@ dependencies = [ [[package]] name = "http" -version = "1.0.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b32afd38673a8016f7c9ae69e5af41a58f81b1d31689040f2f1959594ce194ea" +checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" dependencies = [ "bytes", "fnv", @@ -1977,49 +1991,38 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.6" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 0.2.11", - "pin-project-lite", -] - -[[package]] -name = "http-body" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" -dependencies = [ - "bytes", - "http 1.0.0", + "http 1.2.0", ] [[package]] name = "http-body-util" -version = "0.1.0" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41cb79eb393015dadd30fc252023adb0b2400a0caee0fa2a077e6e21a551e840" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", "futures-util", - "http 1.0.0", - "http-body 1.0.0", + "http 1.2.0", + "http-body", "pin-project-lite", ] [[package]] name = "http-range-header" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ce4ef31cda248bbdb6e6820603b82dfcd9e833db65a43e997a0ccec777d11fe" +checksum = "9171a2ea8a68358193d15dd5d70c1c10a2afc3e7e4c5bc92bc9f025cebd7359c" [[package]] name = "httparse" -version = "1.8.0" +version = "1.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" [[package]] name = "httpdate" @@ -2044,40 +2047,16 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.28" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2 0.3.24", - "http 0.2.11", - "http-body 0.4.6", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "186548d73ac615b32a73aafe38fb4f56c0d340e110e5a200bcadbaf2e199263a" +checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0" dependencies = [ "bytes", "futures-channel", "futures-util", - "h2 0.4.2", - "http 1.0.0", - "http-body 1.0.0", + "h2", + "http 1.2.0", + "http-body", "httparse", "httpdate", "itoa", @@ -2088,57 +2067,93 @@ dependencies = [ ] [[package]] -name = "hyper-rustls" -version = "0.24.2" +name = "hyper-named-pipe" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +checksum = "73b7d8abf35697b81a825e386fc151e0d503e8cb5fcb93cc8669c376dfd6f278" +dependencies = [ + "hex 0.4.3", + "hyper", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", + "winapi", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" dependencies = [ "futures-util", - "http 0.2.11", - "hyper 0.14.28", - "rustls 0.21.10", + "http 1.2.0", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", "tokio", - "tokio-rustls 0.24.1", + "tokio-rustls", + "tower-service", + "webpki-roots", ] [[package]] name = "hyper-tls" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", - "hyper 0.14.28", + "http-body-util", + "hyper", + "hyper-util", "native-tls", "tokio", "tokio-native-tls", + "tower-service", ] [[package]] name = "hyper-util" -version = "0.1.3" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" dependencies = [ "bytes", "futures-channel", "futures-util", - "http 1.0.0", - "http-body 1.0.0", - "hyper 1.2.0", + "http 1.2.0", + "http-body", + "hyper", "pin-project-lite", "socket2", "tokio", - "tower", "tower-service", "tracing", ] [[package]] -name = "iana-time-zone" -version = "0.1.60" +name = "hyperlocal" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +checksum = "986c5ce3b994526b3cd75578e62554abd09f0899d6206de48b3e96ab34ccc8c7" +dependencies = [ + "hex 0.4.3", + "http-body-util", + "hyper", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -2157,22 +2172,130 @@ dependencies = [ "cc", ] +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.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 2.0.91", +] + [[package]] name = "ident_case" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" -[[package]] -name = "idna" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "idna" version = "0.5.0" @@ -2184,22 +2307,37 @@ dependencies = [ ] [[package]] -name = "if_chain" -version = "1.0.2" +name = "idna" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" +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 = "ignore" -version = "0.4.22" +version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b46810df39e66e925525d6e38ce1e7f6e1d208f72dc39757880fcb66e2c58af1" +checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b" dependencies = [ "crossbeam-deque", "globset", "log", "memchr", - "regex-automata 0.4.5", + "regex-automata 0.4.9", "same-file", "walkdir", "winapi-util", @@ -2207,18 +2345,18 @@ dependencies = [ [[package]] name = "include_dir" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18762faeff7122e89e0857b02f7ce6fcc0d101d5e9ad2ad7846cc01d61b7f19e" +checksum = "923d117408f1e49d914f1a379a309cffe4f18c05cf4e3d12e613a15fc81bd0dd" dependencies = [ "include_dir_macros", ] [[package]] name = "include_dir_macros" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b139284b5cf57ecfa712bcc66950bb635b31aff41c188e8a4cfc758eca374a3f" +checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75" dependencies = [ "proc-macro2", "quote", @@ -2243,12 +2381,13 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.3" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ "equivalent", - "hashbrown 0.14.3", + "hashbrown 0.15.2", + "serde", ] [[package]] @@ -2259,24 +2398,14 @@ checksum = "0122b7114117e64a63ac49f752a5ca4624d534c7b1c7de796ac196381cd2d947" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", -] - -[[package]] -name = "inout" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" -dependencies = [ - "block-padding", - "generic-array", + "syn 2.0.91", ] [[package]] name = "insta" -version = "1.35.1" +version = "1.41.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c985c1bef99cf13c58fade470483d81a2bfe846ebde60ed28cc2dddec2df9e2" +checksum = "7e9ffc4d4892617c50a928c52b2961cb5174b6fc6ebf252b2fac9d21955c48b8" dependencies = [ "console", "lazy_static", @@ -2286,26 +2415,52 @@ dependencies = [ "regex", "serde", "similar", - "yaml-rust", +] + +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", ] [[package]] name = "ipnet" -version = "2.9.0" +version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" + +[[package]] +name = "ipnetwork" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf466541e9d546596ee94f9f69590f89473455f88372423e0008fc1a7daf100e" +dependencies = [ + "serde", +] [[package]] name = "is-terminal" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" dependencies = [ - "hermit-abi", + "hermit-abi 0.4.0", "libc", "windows-sys 0.52.0", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "itertools" version = "0.10.5" @@ -2325,25 +2480,50 @@ dependencies = [ ] [[package]] -name = "itoa" -version = "1.0.10" +name = "itertools" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" + +[[package]] +name = "jobserver" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +dependencies = [ + "libc", +] + +[[package]] +name = "jpeg-encoder" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cf3affe27ffd9f1992690ec7575568b222abe9cb39738f6531968aca8e64906" [[package]] name = "js-sys" -version = "0.3.68" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee" +checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" dependencies = [ + "once_cell", "wasm-bindgen", ] [[package]] name = "jsonwebtoken" -version = "9.2.0" +version = "9.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c7ea04a7c5c055c175f189b6dc6ba036fd62306b58c66c9f6389036c503a3f4" +checksum = "b9ae10193d25051e74945f1ea2d0b42e03cc3b890f7e4cc5faa44997d808193f" dependencies = [ "base64 0.21.7", "js-sys", @@ -2355,22 +2535,161 @@ dependencies = [ ] [[package]] -name = "lazy_static" -version = "1.4.0" +name = "jxl-bitstream" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "76c3205d18cf6229b3f694de66e592886ff7afb0740bc0e85594e3b28d6f6622" dependencies = [ - "spin 0.5.2", + "tracing", +] + +[[package]] +name = "jxl-coding" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7075600c762c1ce9e2dd1fbc3fa8ded2af1401fe2221449eca943977742dd0b4" +dependencies = [ + "jxl-bitstream", +] + +[[package]] +name = "jxl-color" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9084bc33b6d01e26b1db6c187514a51a57f4e3780335f3120ab55ee0b08f6e73" +dependencies = [ + "jxl-bitstream", + "jxl-coding", + "jxl-grid", +] + +[[package]] +name = "jxl-frame" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d63bdd104e3746669a123de86f940aa5d59fdc9c5a65f16a4f867dde75e45e1" +dependencies = [ + "jxl-bitstream", + "jxl-coding", + "jxl-grid", + "jxl-image", + "jxl-modular", + "jxl-vardct", + "tracing", +] + +[[package]] +name = "jxl-grid" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48800b21ed6bb3bbc2f818ae9cd40530bdfb1a211f57d5a7a49b8b10f62145e8" + +[[package]] +name = "jxl-image" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef86f7f74acc9c9e66604c8d030e00cdef5a0c455ea3d7d26bd9ddbb9160be59" +dependencies = [ + "jxl-bitstream", + "jxl-color", + "jxl-grid", + "tracing", +] + +[[package]] +name = "jxl-modular" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "504e6b55db362568592be81993c772fc6786c56fb67ae769ff62dc514c3e6748" +dependencies = [ + "jxl-bitstream", + "jxl-coding", + "jxl-grid", + "tracing", +] + +[[package]] +name = "jxl-oxide" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57e3b7e459d823979c4ca0c9584f391581db154437f34596ea443b082e9b6064" +dependencies = [ + "jxl-bitstream", + "jxl-color", + "jxl-frame", + "jxl-grid", + "jxl-image", + "jxl-render", + "tracing", +] + +[[package]] +name = "jxl-render" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7157d1c6c4896ddc800cb0cc8ba545ba7417ab9afc51f39e69484e6607c8707e" +dependencies = [ + "jxl-bitstream", + "jxl-coding", + "jxl-color", + "jxl-frame", + "jxl-grid", + "jxl-image", + "jxl-modular", + "jxl-vardct", + "tracing", +] + +[[package]] +name = "jxl-vardct" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb4a2d9ba8c48a52f6143ba01c38aac67d1309c9b939a9f84cd60f650d15053e" +dependencies = [ + "jxl-bitstream", + "jxl-coding", + "jxl-grid", + "jxl-modular", + "tracing", +] + +[[package]] +name = "kamadak-exif" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef4fc70d0ab7e5b6bafa30216a6b48705ea964cdfc29c050f2412295eba58077" +dependencies = [ + "mutate_once", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] + +[[package]] +name = "leaky-bucket" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a396bb213c2d09ed6c5495fd082c991b6ab39c9daf4fff59e6727f85c73e4c5" +dependencies = [ + "parking_lot 0.12.3", + "pin-project-lite", + "tokio", ] [[package]] name = "lettre" -version = "0.11.4" +version = "0.11.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357ff5edb6d8326473a64c82cf41ddf78ab116f89668c50c4fac1b321e5e80f4" +checksum = "ab4c9a167ff73df98a5ecc07e8bf5ce90b583665da3d1762eb1f775ad4d0d6f5" dependencies = [ "async-trait", - "base64 0.21.7", + "base64 0.22.1", "chumsky", "email-encoding", "email_address", @@ -2379,50 +2698,52 @@ dependencies = [ "futures-util", "hostname", "httpdate", - "idna 0.5.0", + "idna 1.0.3", "mime", "nom", "percent-encoding", "quoted_printable", - "rustls 0.22.2", - "rustls-pemfile 2.1.0", + "rustls", + "rustls-pemfile", + "rustls-pki-types", "socket2", "tokio", - "tokio-rustls 0.25.0", + "tokio-rustls", "url", - "webpki-roots 0.26.1", + "webpki-roots", ] [[package]] name = "libc" -version = "0.2.153" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "libm" -version = "0.2.8" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" [[package]] name = "libredox" -version = "0.0.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.6.0", "libc", - "redox_syscall", + "redox_syscall 0.5.8", ] [[package]] name = "librqbit-bencode" -version = "2.2.1" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25214563468dda753fbae62f5ed3b1def14219f2faa416f76f7a449efb8a8092" +checksum = "41523d6b59f316b5e9680c0470a5ee00f1a43e463dbcb1b7a02eab56c87b8e80" dependencies = [ "anyhow", + "bytes", "librqbit-buffers", "librqbit-clone-to-owned", "librqbit-sha1-wrapper", @@ -2431,34 +2752,40 @@ dependencies = [ [[package]] name = "librqbit-buffers" -version = "2.2.1" +version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "378dc12b4994dec9edf0558e39144972167569458f837dc0c67ddeb044ff9a00" +checksum = "e1635d74bbdaaa4bca7c432879492a609694219829b4324ec845f7e60a703f41" dependencies = [ + "bytes", "librqbit-clone-to-owned", "serde", ] [[package]] name = "librqbit-clone-to-owned" -version = "2.2.1" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c33f149bc6cef41a9f24ad43ece20c87e0617fc88affa01d95850eb68210daac" +checksum = "4dabe584079813bc1bb675eb8cddfcf378e97590e5927af594e7596b0946757d" +dependencies = [ + "bytes", +] [[package]] name = "librqbit-core" -version = "3.5.0" +version = "4.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d055eac3cd062d4b7728feccb45495682b834305bf234f275440b32814276a65" +checksum = "7eebb00603f98c4fe153f527ce08cd5307040ceb03db074af5d61a4946768c9c" dependencies = [ "anyhow", + "bytes", + "data-encoding", "directories", "hex 0.4.3", "itertools 0.12.1", "librqbit-bencode", "librqbit-buffers", "librqbit-clone-to-owned", - "parking_lot", + "parking_lot 0.12.3", "serde", "tokio", "tokio-util", @@ -2469,18 +2796,18 @@ dependencies = [ [[package]] name = "librqbit-sha1-wrapper" -version = "2.2.1" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45016d84e0f1751ad9b645378117adf648dd6649e1b371a398862cd6a10356fe" +checksum = "29b096c4cfd6b8a4b56d0231afd1c7047fadab807d8c0c4152152725dcf4b6a3" dependencies = [ "crypto-hash", ] [[package]] name = "libsqlite3-sys" -version = "0.27.0" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4e226dcd58b4be396f7bd3c20da8fdee2911400705297ba7d2d7cc2c30f716" +checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" dependencies = [ "cc", "pkg-config", @@ -2489,20 +2816,22 @@ dependencies = [ [[package]] name = "lightningcss" -version = "1.0.0-alpha.54" +version = "1.0.0-alpha.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07d306844e5af1753490c420c0d6ae3d814b00725092d106332762827ca8f0fe" +checksum = "20c9e1f991b3861d25bf872ecca2eb6a73f7a9fe671da047cd1f9b49c65cbc40" dependencies = [ - "ahash 0.8.9", - "bitflags 2.4.2", + "ahash 0.8.11", + "bitflags 2.6.0", "const-str", - "cssparser", + "cssparser 0.33.0", "cssparser-color", "dashmap", "data-encoding", "getrandom", + "indexmap 2.7.0", "itertools 0.10.5", "lazy_static", + "lightningcss-derive", "parcel_selectors", "parcel_sourcemap", "paste", @@ -2512,6 +2841,18 @@ dependencies = [ "smallvec", ] +[[package]] +name = "lightningcss-derive" +version = "1.0.0-alpha.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c12744d1279367caed41739ef094c325d53fb0ffcd4f9b84a368796f870252" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "linked-hash-map" version = "0.5.6" @@ -2520,25 +2861,49 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "litemap" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", ] [[package]] -name = "loco-rs" -version = "0.3.1" +name = "loco-gen" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "629f92003dc023b90671f90869c5170145597ac0ca94296a6dacde85758fc566" +checksum = "b918975ceca60b4fc09180b0431efbe236c7b9cf29393130cac7572ba22c7cc4" +dependencies = [ + "chrono", + "clap", + "dialoguer", + "duct", + "regex", + "rrgen", + "serde", + "serde_json", + "thiserror 1.0.69", + "tracing", +] + +[[package]] +name = "loco-rs" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda11695a517c1a51e5ac44eb4ea8b76d593c29544ca4523d8885279d0adfa22" dependencies = [ "argon2", "async-trait", @@ -2549,49 +2914,64 @@ dependencies = [ "bb8", "byte-unit", "bytes", - "cargo_metadata", "cfg-if", "chrono", "clap", "colored", "duct", - "eyre", + "duct_sh", + "english-to-cron", "fs-err", "futures-util", - "hyper 1.2.0", + "heck 0.4.1", + "hyper", "include_dir", + "ipnetwork", "jsonwebtoken", - "lazy_static", "lettre", + "loco-gen", "mime", + "moka", "object_store", "rand", "regex", - "requestty", - "rrgen", "rusty-sidekiq", "sea-orm", "sea-orm-migration", + "semver", "serde", "serde_json", "serde_variant", "serde_yaml", + "sqlx", "tera", - "thiserror", + "thiserror 1.0.69", + "thousands", "tokio", - "tower", + "tokio-cron-scheduler", + "tokio-util", + "toml", + "tower 0.4.13", "tower-http", "tracing", + "tracing-appender", "tracing-subscriber", + "ulid", "uuid", - "validator", + "validator 0.18.1", ] [[package]] name = "log" -version = "0.4.20" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "mac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" [[package]] name = "maplit" @@ -2600,10 +2980,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" [[package]] -name = "match_cfg" -version = "0.1.0" +name = "markup5ever" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" +checksum = "82c88c6129bd24319e62a0359cb6b958fa7e8be6e19bb1663bc396b90883aca5" +dependencies = [ + "log", + "phf", + "phf_codegen", + "string_cache", + "string_cache_codegen", + "tendril", +] [[package]] name = "matchers" @@ -2626,6 +3014,12 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" +[[package]] +name = "matchit" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0aa4b8ca861b08d68afc8702af3250776898c1508b278e1da9d01e01d4b45c" + [[package]] name = "md-5" version = "0.10.6" @@ -2638,9 +3032,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.1" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "mime" @@ -2650,9 +3044,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "mime_guess" -version = "2.0.4" +version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" dependencies = [ "mime", "unicase", @@ -2666,23 +3060,22 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.2" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394" dependencies = [ - "adler", + "adler2", ] [[package]] name = "mio" -version = "0.8.10" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ "libc", - "log", "wasi", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -2692,12 +3085,54 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d95ee98a292cf91c2f5b3f35424773af16842a68b3be33b389137606b2633539" [[package]] -name = "native-tls" -version = "0.2.11" +name = "moka" +version = "0.12.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +checksum = "32cf62eb4dd975d2dde76432fb1075c49e3ee2331cf36f1f8fd4b66550d32b6f" +dependencies = [ + "crossbeam-channel", + "crossbeam-epoch", + "crossbeam-utils", + "once_cell", + "parking_lot 0.12.3", + "quanta", + "rustc_version", + "smallvec", + "tagptr", + "thiserror 1.0.69", + "triomphe", + "uuid", +] + +[[package]] +name = "multer" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b" +dependencies = [ + "bytes", + "encoding_rs", + "futures-util", + "http 1.2.0", + "httparse", + "memchr", + "mime", + "spin", + "version_check", +] + +[[package]] +name = "mutate_once" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16cf681a23b4d0a43fc35024c176437f9dcd818db34e0f42ab456a0ee5ad497b" + +[[package]] +name = "native-tls" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" dependencies = [ - "lazy_static", "libc", "log", "openssl", @@ -2716,15 +3151,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c96aba5aa877601bb3f6dd6a63a969e1f82e60646e81e71b14496995e9853c91" [[package]] -name = "nix" -version = "0.27.1" +name = "new_debug_unreachable" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" -dependencies = [ - "bitflags 2.4.2", - "cfg-if", - "libc", -] +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" [[package]] name = "nom" @@ -2748,11 +3178,10 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ - "autocfg", "num-integer", "num-traits", ] @@ -2780,6 +3209,17 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", +] + [[package]] name = "num-integer" version = "0.1.46" @@ -2791,9 +3231,9 @@ dependencies = [ [[package]] name = "num-iter" -version = "0.1.44" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" dependencies = [ "autocfg", "num-integer", @@ -2802,9 +3242,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", "libm", @@ -2816,32 +3256,32 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.9", "libc", ] [[package]] name = "object" -version = "0.32.2" +version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "memchr", ] [[package]] name = "object_store" -version = "0.9.0" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d139f545f64630e2e3688fd9f81c470888ab01edeb72d13b4e86c566f1130000" +checksum = "3cfccb68961a56facde1163f9319e0d15743352344e7808a11795fb99698dcaf" dependencies = [ "async-trait", "bytes", "chrono", "futures", "humantime", - "itertools 0.12.1", - "parking_lot", + "itertools 0.13.0", + "parking_lot 0.12.3", "percent-encoding", "snafu", "tokio", @@ -2852,47 +3292,44 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "opendal" -version = "0.45.0" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3350be0d4ba326017ce22c98a9e94d21b069160fcd95bbe6c2555dac4e93c47a" +checksum = "6c8cd8697b917793c15a7b4a8afcba44e35e2abbc55c363064851776f7c81136" dependencies = [ "anyhow", "async-trait", "backon", - "base64 0.21.7", + "base64 0.22.1", "bytes", "chrono", - "flagset", "futures", "getrandom", - "http 0.2.11", + "http 1.2.0", "log", "md-5", "once_cell", "percent-encoding", - "quick-xml 0.30.0", - "reqsign", + "quick-xml 0.36.2", "reqwest", "serde", "serde_json", - "sha2", "tokio", "uuid", ] [[package]] name = "openssl" -version = "0.10.64" +version = "0.10.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.6.0", "cfg-if", "foreign-types", "libc", @@ -2909,7 +3346,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.91", ] [[package]] @@ -2920,9 +3357,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.101" +version = "0.9.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dda2b0f344e78efc2facf7d195d098df0dd72151b26ab98da807afc26c198dff" +checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" dependencies = [ "cc", "libc", @@ -2954,31 +3391,21 @@ dependencies = [ "num-traits", ] -[[package]] -name = "ordered-multimap" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4d6a8c22fc714f0c2373e6091bf6f5e9b37b1bc0b1184874b7e0a4e303d318f" -dependencies = [ - "dlv-list", - "hashbrown 0.14.3", -] - [[package]] name = "os_pipe" -version = "1.1.5" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57119c3b893986491ec9aa85056780d3a0f3cf4da7cc09dd3650dbd6c6738fb9" +checksum = "5ffd2b0a5634335b135d5728d84c5e0fd726954b87111f7506a61c502280d982" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "ouroboros" -version = "0.17.2" +version = "0.18.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2ba07320d39dfea882faa70554b4bd342a5f273ed59ba7c1c6b4c840492c954" +checksum = "944fa20996a25aded6b4795c6d63f10014a7a83f8be9828a11860b08c5fc4a67" dependencies = [ "aliasable", "ouroboros_macro", @@ -2987,15 +3414,16 @@ dependencies = [ [[package]] name = "ouroboros_macro" -version = "0.17.2" +version = "0.18.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec4c6225c69b4ca778c0aea097321a64c421cf4577b331c61b229267edabb6f8" +checksum = "39b0deead1528fd0e5947a8546a9642a9777c25f6e1e26f34c97b204bbb465bd" dependencies = [ - "heck", - "proc-macro-error", + "heck 0.4.1", + "itertools 0.12.1", "proc-macro2", + "proc-macro2-diagnostics", "quote", - "syn 2.0.50", + "syn 2.0.91", ] [[package]] @@ -3012,17 +3440,17 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "parcel_selectors" -version = "0.26.4" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05d74befe2d076330d9a58bf9ca2da424568724ab278adf15fb5718253133887" +checksum = "7645c578d3a5c4cdf667af1ad39765f5f751c4883d251e050d5e1204b5cad0a9" dependencies = [ - "bitflags 2.4.2", - "cssparser", - "fxhash", + "bitflags 2.6.0", + "cssparser 0.33.0", "log", - "phf 0.10.1", - "phf_codegen 0.10.0", + "phf", + "phf_codegen", "precomputed-hash", + "rustc-hash", "smallvec", ] @@ -3041,33 +3469,89 @@ dependencies = [ ] [[package]] -name = "parking_lot" -version = "0.12.1" +name = "parking" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core 0.8.6", +] + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", - "parking_lot_core", + "parking_lot_core 0.9.10", ] [[package]] name = "parking_lot_core" -version = "0.9.9" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall 0.2.16", + "smallvec", + "winapi", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.5.8", "smallvec", - "windows-targets 0.48.5", + "windows-targets 0.52.6", +] + +[[package]] +name = "parse-display" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "914a1c2265c98e2446911282c6ac86d8524f495792c38c5bd884f80499c7538a" +dependencies = [ + "parse-display-derive", + "regex", + "regex-syntax 0.8.5", +] + +[[package]] +name = "parse-display-derive" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ae7800a4c974efd12df917266338e79a7a74415173caf7e70aa0a0707345281" +dependencies = [ + "proc-macro2", + "quote", + "regex", + "regex-syntax 0.8.5", + "structmeta", + "syn 2.0.91", ] [[package]] name = "parse-zoneinfo" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c705f256449c60da65e11ff6626e0c16a0a0b96aaa348de61376b249bc340f41" +checksum = "1f2a05b18d44e2957b88f96ba460715e295bc1d7510468a2f3d3b44535d26c24" dependencies = [ "regex", ] @@ -3085,33 +3569,23 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "pathdiff" -version = "0.2.1" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" - -[[package]] -name = "pbkdf2" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" -dependencies = [ - "digest", - "hmac", -] +checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" [[package]] name = "pem" -version = "3.0.3" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b8fcc794035347fb64beda2d3b462595dd2753e3f268d89c5aae77e8cf2c310" +checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae" dependencies = [ - "base64 0.21.7", + "base64 0.22.1", "serde", ] @@ -3132,20 +3606,20 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.7" +version = "2.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "219c0dcc30b6a27553f9cc242972b67f75b60eb0db71f0b5462f38b058c41546" +checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc" dependencies = [ "memchr", - "thiserror", + "thiserror 2.0.9", "ucd-trie", ] [[package]] name = "pest_derive" -version = "2.7.7" +version = "2.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e1288dbd7786462961e69bfd4df7848c1e37e8b74303dbdab82c3a9cdd2809" +checksum = "816518421cfc6887a0d62bf441b6ffb4536fcc926395a69e1a85852d4363f57e" dependencies = [ "pest", "pest_generator", @@ -3153,37 +3627,28 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.7" +version = "2.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1381c29a877c6d34b8c176e734f35d7f7f5b3adaefe940cb4d1bb7af94678e2e" +checksum = "7d1396fd3a870fc7838768d171b4616d5c91f6cc25e377b673d714567d99377b" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.91", ] [[package]] name = "pest_meta" -version = "2.7.7" +version = "2.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0934d6907f148c22a3acbda520c7eed243ad7487a30f51f6ce52b58b7077a8a" +checksum = "e1e58089ea25d717bfd31fb534e4f3afcc2cc569c70de3e239778991ea3b7dea" dependencies = [ "once_cell", "pest", "sha2", ] -[[package]] -name = "phf" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" -dependencies = [ - "phf_shared 0.10.0", -] - [[package]] name = "phf" version = "0.11.2" @@ -3194,16 +3659,6 @@ dependencies = [ "phf_shared 0.11.2", ] -[[package]] -name = "phf_codegen" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" -dependencies = [ - "phf_generator 0.10.0", - "phf_shared 0.10.0", -] - [[package]] name = "phf_codegen" version = "0.11.2" @@ -3244,7 +3699,7 @@ dependencies = [ "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.91", ] [[package]] @@ -3265,31 +3720,11 @@ dependencies = [ "siphasher", ] -[[package]] -name = "pin-project" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0302c4a0442c456bd56f841aee5c3bfd17967563f6fadc9ceb9f9c23cf3807e0" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "266c042b60c9c76b8d53061e52b2e0d1116abc57cefc8c5cd671619a56ac3690" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.50", -] - [[package]] name = "pin-project-lite" -version = "0.2.13" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" [[package]] name = "pin-utils" @@ -3308,21 +3743,6 @@ dependencies = [ "spki", ] -[[package]] -name = "pkcs5" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e847e2c91a18bfa887dd028ec33f2fe6f25db77db3619024764914affe8b69a6" -dependencies = [ - "aes", - "cbc", - "der", - "pbkdf2", - "scrypt", - "sha2", - "spki", -] - [[package]] name = "pkcs8" version = "0.10.2" @@ -3330,16 +3750,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ "der", - "pkcs5", - "rand_core", "spki", ] [[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" @@ -3349,9 +3767,12 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] [[package]] name = "precomputed-hash" @@ -3361,9 +3782,9 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "pretty_assertions" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" +checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" dependencies = [ "diff", "yansi", @@ -3371,9 +3792,9 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "3.1.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" dependencies = [ "toml_edit", ] @@ -3403,19 +3824,54 @@ dependencies = [ ] [[package]] -name = "proc-macro2" -version = "1.0.78" +name = "proc-macro-error-attr2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "proc-macro2" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] [[package]] -name = "psm" -version = "0.1.21" +name = "proc-macro2-diagnostics" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" +checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", + "version_check", + "yansi", +] + +[[package]] +name = "psm" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "200b9ff220857e53e184257720a14553b2f4aa02577d2ed9842d45d4b9654810" dependencies = [ "cc", ] @@ -3442,42 +3898,109 @@ dependencies = [ [[package]] name = "qbit-rs" -version = "0.4.1" -source = "git+https://github.com/George-Miao/qbit.git?rev=ad5af6a#ad5af6a55b93b2c91b17d12d1b2ce54537df2355" +version = "0.4.6" +source = "git+https://github.com/lonelyhentxi/qbit.git?rev=a2c70aa#a2c70aa391d5edc2ab79c92fa8dcfec00d0d714b" dependencies = [ + "bytes", "mod_use", "reqwest", "serde", "serde-value", "serde_json", "serde_repr", - "serde_with 2.3.3", + "serde_with", "tap", - "thiserror", + "thiserror 2.0.9", "tracing", "typed-builder", "url", ] [[package]] -name = "quick-xml" -version = "0.30.0" +name = "quanta" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eff6510e86862b57b210fd8cbe8ed3f0d7d600b9c2863cd4549a2e033c66e956" +checksum = "773ce68d0bb9bc7ef20be3536ffe94e223e1f365bd374108b2659fac0c65cfe6" +dependencies = [ + "crossbeam-utils", + "libc", + "once_cell", + "raw-cpuid", + "wasi", + "web-sys", + "winapi", +] + +[[package]] +name = "quick-xml" +version = "0.36.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7649a7b4df05aed9ea7ec6f628c67c9953a43869b8bc50929569b2999d443fe" dependencies = [ - "encoding_rs", "memchr", "serde", ] [[package]] name = "quick-xml" -version = "0.31.0" +version = "0.37.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33" +checksum = "f22f29bdff3987b4d8632ef95fd6424ec7e4e0a57e2f4fc63e489e75357f6a03" dependencies = [ + "encoding_rs", "memchr", - "serde", +] + +[[package]] +name = "quinn" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62e96808277ec6f97351a2380e6c25114bc9e67037775464979f3037c92d05ef" +dependencies = [ + "bytes", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror 2.0.9", + "tokio", + "tracing", +] + +[[package]] +name = "quinn-proto" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2fe5ef3495d7d2e377ff17b1a8ce2ee2ec2a18cde8b6ad6619d65d0701c135d" +dependencies = [ + "bytes", + "getrandom", + "rand", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror 2.0.9", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c40286217b4ba3a71d644d752e6a0b71f13f1b6a2c5311acfcbe0c2418ed904" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.59.0", ] [[package]] @@ -3487,24 +4010,24 @@ dependencies = [ "nom", "percent-encoding", "serde", - "thiserror", + "thiserror 1.0.69", "url", ] [[package]] name = "quote" -version = "1.0.35" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] [[package]] name = "quoted_printable" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79ec282e887b434b68c18fe5c121d38e72a5cf35119b59e54ec5b992ea9c8eb0" +checksum = "640c9bd8497b02465aeef5375144c26062e0dcd5939dfcbb0f5db76cb8c17c73" [[package]] name = "radium" @@ -3543,10 +4066,19 @@ dependencies = [ ] [[package]] -name = "rayon" -version = "1.9.0" +name = "raw-cpuid" +version = "11.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4963ed1bc86e4f3ee217022bd855b297cef07fb9eac5dfa1f788b220b49b3bd" +checksum = "1ab240315c661615f2ee9f0f2cd32d5a7343a84d5ebcccb99d46e6637565e7b0" +dependencies = [ + "bitflags 2.6.0", +] + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ "either", "rayon-core", @@ -3576,34 +4108,40 @@ dependencies = [ "html-escape", "include_dir", "insta", - "itertools 0.12.1", + "itertools 0.13.0", "lazy_static", - "librqbit-core", + "leaky-bucket", "lightningcss", "loco-rs", "maplit", + "once_cell", "opendal", - "qbit-rs", "quirks_path", "regex", "reqwest", + "reqwest-middleware", + "reqwest-retry", + "reqwest-tracing", "rss", "rstest", + "scraper", "sea-orm", "sea-orm-migration", "serde", "serde_json", + "serde_with", "serial_test", "testcontainers", "testcontainers-modules", - "thiserror", - "tl", + "thiserror 2.0.9", "tokio", + "torrent", "tracing", "tracing-subscriber", "url", "uuid", - "validator", + "validator 0.19.0", + "zune-image", ] [[package]] @@ -3628,34 +4166,52 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.4.1" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ "bitflags 1.3.2", ] [[package]] -name = "redox_users" -version = "0.4.4" +name = "redox_syscall" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" +dependencies = [ + "bitflags 2.6.0", +] + +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ "getrandom", "libredox", - "thiserror", + "thiserror 1.0.69", ] [[package]] name = "regex" -version = "1.10.3" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.5", - "regex-syntax 0.8.2", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", ] [[package]] @@ -3669,13 +4225,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.5" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.2", + "regex-syntax 0.8.5", ] [[package]] @@ -3686,15 +4242,15 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.2" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "relative-path" -version = "1.9.2" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e898588f33fdd5b9420719948f9f2a32c922a246964576f71ba7f24f80610fbc" +checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" [[package]] name = "rend" @@ -3705,91 +4261,38 @@ dependencies = [ "bytecheck", ] -[[package]] -name = "reqsign" -version = "0.14.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43e319d9de9ff4d941abf4ac718897118b0fe04577ea3f8e0f5788971784eef5" -dependencies = [ - "anyhow", - "async-trait", - "base64 0.21.7", - "chrono", - "form_urlencoded", - "getrandom", - "hex 0.4.3", - "hmac", - "home", - "http 0.2.11", - "jsonwebtoken", - "log", - "once_cell", - "percent-encoding", - "quick-xml 0.31.0", - "rand", - "reqwest", - "rsa", - "rust-ini", - "serde", - "serde_json", - "sha1", - "sha2", -] - -[[package]] -name = "requestty" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa883a1f3e288e65187f653e6ba2e84fdf810fe02f4c8074f9c723d1aa26e2ae" -dependencies = [ - "requestty-ui", - "shell-words", - "smallvec", - "tempfile", - "winsplit", -] - -[[package]] -name = "requestty-ui" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7549bab39cf982b629b68e7ec191a5574e85086e95c0ebe514c02d3b42ffe225" -dependencies = [ - "bitflags 1.3.2", - "crossterm", - "once_cell", - "textwrap", - "unicode-segmentation", -] - [[package]] name = "reqwest" -version = "0.11.24" +version = "0.12.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6920094eb85afde5e4a138be3f2de8bbdf28000f0029e72c45025a56b042251" +checksum = "7fe060fe50f524be480214aba758c71f99f90ee8c83c5a36b5e9e1d568eb4eb3" dependencies = [ - "base64 0.21.7", + "base64 0.22.1", "bytes", "encoding_rs", "futures-core", "futures-util", - "h2 0.3.24", - "http 0.2.11", - "http-body 0.4.6", - "hyper 0.14.28", + "h2", + "http 1.2.0", + "http-body", + "http-body-util", + "hyper", "hyper-rustls", "hyper-tls", + "hyper-util", "ipnet", "js-sys", "log", "mime", + "mime_guess", "native-tls", "once_cell", "percent-encoding", "pin-project-lite", - "rustls 0.21.10", - "rustls-native-certs", - "rustls-pemfile 1.0.4", + "quinn", + "rustls", + "rustls-pemfile", + "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", @@ -3797,15 +4300,70 @@ dependencies = [ "system-configuration", "tokio", "tokio-native-tls", - "tokio-rustls 0.24.1", + "tokio-rustls", "tokio-util", + "tower 0.5.2", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "wasm-streams", "web-sys", - "winreg", + "webpki-roots", + "windows-registry", +] + +[[package]] +name = "reqwest-middleware" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1ccd3b55e711f91a9885a2fa6fbbb2e39db1776420b062efc058c6410f7e5e3" +dependencies = [ + "anyhow", + "async-trait", + "http 1.2.0", + "reqwest", + "serde", + "thiserror 1.0.69", + "tower-service", +] + +[[package]] +name = "reqwest-retry" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c73e4195a6bfbcb174b790d9b3407ab90646976c55de58a6515da25d851178" +dependencies = [ + "anyhow", + "async-trait", + "futures", + "getrandom", + "http 1.2.0", + "hyper", + "parking_lot 0.11.2", + "reqwest", + "reqwest-middleware", + "retry-policies", + "thiserror 1.0.69", + "tokio", + "tracing", + "wasm-timer", +] + +[[package]] +name = "reqwest-tracing" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73e6153390585f6961341b50e5a1931d6be6dee4292283635903c26ef9d980d2" +dependencies = [ + "anyhow", + "async-trait", + "getrandom", + "http 1.2.0", + "matchit 0.8.5", + "reqwest", + "reqwest-middleware", + "tracing", ] [[package]] @@ -3815,7 +4373,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9838134a2bfaa8e1f40738fcc972ac799de6e0e06b5157acb95fc2b05a0ea283" dependencies = [ "lazy_static", - "thiserror", + "thiserror 1.0.69", +] + +[[package]] +name = "retry-policies" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5875471e6cab2871bc150ecb8c727db5113c9338cc3354dc5ee3425b6aa40a1c" +dependencies = [ + "rand", ] [[package]] @@ -3828,16 +4395,16 @@ dependencies = [ "cfg-if", "getrandom", "libc", - "spin 0.9.8", + "spin", "untrusted", "windows-sys 0.52.0", ] [[package]] name = "rkyv" -version = "0.7.44" +version = "0.7.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cba464629b3394fc4dbc6f940ff8f5b4ff5c7aef40f29166fd4ad12acbc99c0" +checksum = "9008cd6385b9e161d8229e1f6549dd23c3d022f132a2ea37ac3a10ac4935779b" dependencies = [ "bitvec", "bytecheck", @@ -3853,9 +4420,9 @@ dependencies = [ [[package]] name = "rkyv_derive" -version = "0.7.44" +version = "0.7.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7dddfff8de25e6f62b9d64e6e432bf1c6736c57d20323e15ee10435fbda7c65" +checksum = "503d1d27590a2b0a3a4ca4c94755aa2875657196ecbf401a42eff41d7de532c0" dependencies = [ "proc-macro2", "quote", @@ -3864,28 +4431,28 @@ dependencies = [ [[package]] name = "rrgen" -version = "0.5.3" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e40013551787f9f535e7dbc8dafc164591d941aeae48881a385d8b0393dd45f6" +checksum = "18e27f5f254d89b0b5b76445442e5c935b63a566ee5a735c9d877ca2029b4ce9" dependencies = [ "cruet", "fs-err", "glob", - "heck", + "heck 0.4.1", "regex", "serde", "serde_json", "serde_regex", "serde_yaml", "tera", - "thiserror", + "thiserror 1.0.69", ] [[package]] name = "rsa" -version = "0.9.6" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" +checksum = "47c75d7c5c6b673e58bf54d8544a9f432e3a925b0e80f7cd3602ab5c50c55519" dependencies = [ "const-oid", "digest", @@ -3895,7 +4462,6 @@ dependencies = [ "pkcs1", "pkcs8", "rand_core", - "sha2", "signature", "spki", "subtle", @@ -3904,21 +4470,21 @@ dependencies = [ [[package]] name = "rss" -version = "2.0.7" +version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7b2c77eb4450d7d5f98df52c381cd6c4e19b75dad9209a9530b85a44510219a" +checksum = "531af70fce504d369cf42ac0a9645f5a62a8ea9265de71cfa25087e9f6080c7c" dependencies = [ "atom_syndication", "derive_builder", "never", - "quick-xml 0.30.0", + "quick-xml 0.37.1", ] [[package]] name = "rstest" -version = "0.18.2" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97eeab2f3c0a199bc4be135c36c924b6590b88c377d416494288c14f2db30199" +checksum = "0a2c585be59b6b5dd66a9d2084aa1d8bd52fbdb806eafdeffb52791147862035" dependencies = [ "futures", "futures-timer", @@ -3928,31 +4494,22 @@ dependencies = [ [[package]] name = "rstest_macros" -version = "0.18.2" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d428f8247852f894ee1be110b375111b586d4fa431f6c46e64ba5a0dcccbe605" +checksum = "825ea780781b15345a146be27eaefb05085e337e869bff01b4306a4fd4a9ad5a" dependencies = [ "cfg-if", "glob", + "proc-macro-crate", "proc-macro2", "quote", "regex", "relative-path", "rustc_version", - "syn 2.0.50", + "syn 2.0.91", "unicode-ident", ] -[[package]] -name = "rust-ini" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e0698206bcb8882bf2a9ecb4c1e7785db57ff052297085a6efd4fe42302068a" -dependencies = [ - "cfg-if", - "ordered-multimap", -] - [[package]] name = "rust-multipart-rfc7578_2" version = "0.6.1" @@ -3962,18 +4519,18 @@ dependencies = [ "bytes", "futures-core", "futures-util", - "http 0.2.11", + "http 0.2.12", "mime", "mime_guess", "rand", - "thiserror", + "thiserror 1.0.69", ] [[package]] name = "rust_decimal" -version = "1.34.3" +version = "1.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39449a79f45e8da28c57c341891b69a183044b29518bb8f86dbac9df60bb7df" +checksum = "b082d80e3e3cc52b2ed634388d436fe1f4de6af5786cc2de9ba9737527bdf555" dependencies = [ "arrayvec", "borsh", @@ -3987,110 +4544,89 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustc-hash" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" [[package]] name = "rustc_version" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ "semver", ] [[package]] name = "rustix" -version = "0.38.31" +version = "0.38.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" +checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "rustls" -version = "0.21.10" +version = "0.23.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" -dependencies = [ - "log", - "ring", - "rustls-webpki 0.101.7", - "sct", -] - -[[package]] -name = "rustls" -version = "0.22.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e87c9956bd9807afa1f77e0f7594af32566e830e088a5576d27c5b6f30f49d41" +checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b" dependencies = [ "log", + "once_cell", "ring", "rustls-pki-types", - "rustls-webpki 0.102.2", + "rustls-webpki", "subtle", "zeroize", ] [[package]] name = "rustls-native-certs" -version = "0.6.3" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" +checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" dependencies = [ "openssl-probe", - "rustls-pemfile 1.0.4", + "rustls-pemfile", + "rustls-pki-types", "schannel", "security-framework", ] [[package]] name = "rustls-pemfile" -version = "1.0.4" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" dependencies = [ - "base64 0.21.7", -] - -[[package]] -name = "rustls-pemfile" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c333bb734fcdedcea57de1602543590f545f127dc8b533324318fd492c5c70b" -dependencies = [ - "base64 0.21.7", "rustls-pki-types", ] [[package]] name = "rustls-pki-types" -version = "1.3.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "048a63e5b3ac996d78d402940b5fa47973d2d080c6c6fffa1d0f19c4445310b7" - -[[package]] -name = "rustls-webpki" -version = "0.101.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37" dependencies = [ - "ring", - "untrusted", + "web-time", ] [[package]] name = "rustls-webpki" -version = "0.102.2" +version = "0.102.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" dependencies = [ "ring", "rustls-pki-types", @@ -4099,50 +4635,43 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.14" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" [[package]] name = "rusty-sidekiq" -version = "0.8.2" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a00db3916faeea070039864f98d4fd759d96fc07722571a4918d996fea5621" +checksum = "15544f047600b602c7b11ff7ee0882f9034f9cbe2c205693edd5615e2a6c03ee" dependencies = [ "async-trait", "bb8", "chrono", + "convert_case", "cron_clock", "gethostname", - "heck", "hex 0.4.3", "num_cpus", "rand", "redis", "serde", "serde_json", + "serial_test", "sha2", "slog-term", - "thiserror", + "thiserror 1.0.69", "tokio", + "tokio-util", "tracing", "tracing-subscriber", ] [[package]] name = "ryu" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" - -[[package]] -name = "salsa20" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" -dependencies = [ - "cipher", -] +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "same-file" @@ -4154,12 +4683,21 @@ dependencies = [ ] [[package]] -name = "schannel" -version = "0.1.23" +name = "scc" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +checksum = "94b13f8ea6177672c49d12ed964cca44836f59621981b04a3e26b87e675181de" dependencies = [ - "windows-sys 0.52.0", + "sdd", +] + +[[package]] +name = "schannel" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +dependencies = [ + "windows-sys 0.59.0", ] [[package]] @@ -4169,44 +4707,44 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] -name = "scrypt" -version = "0.11.0" +name = "scraper" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f" +checksum = "cc3d051b884f40e309de6c149734eab57aa8cc1347992710dc80bcc1c2194c15" dependencies = [ - "pbkdf2", - "salsa20", - "sha2", + "cssparser 0.34.0", + "ego-tree", + "getopts", + "html5ever", + "precomputed-hash", + "selectors", + "tendril", ] [[package]] -name = "sct" -version = "0.7.1" +name = "sdd" +version = "3.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" -dependencies = [ - "ring", - "untrusted", -] +checksum = "478f121bb72bbf63c52c93011ea1791dca40140dfe13f8336c4c5ac952c33aa9" [[package]] name = "sea-bae" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bd3534a9978d0aa7edd2808dc1f8f31c4d0ecd31ddf71d997b3c98e9f3c9114" +checksum = "f694a6ab48f14bc063cfadff30ab551d3c7e46d8f81836c51989d548f44a2a25" dependencies = [ - "heck", - "proc-macro-error", + "heck 0.4.1", + "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.91", ] [[package]] name = "sea-orm" -version = "1.0.0-rc.1" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b1218f3b8f95076954bde2e2a33852f873a91e5bbd49402f8fc2bb755c0ed3" +checksum = "0dbcf83248860dc632c46c7e81a221e041b50d0006191756cb001d9e8afc60a9" dependencies = [ "async-stream", "async-trait", @@ -4223,7 +4761,7 @@ dependencies = [ "serde_json", "sqlx", "strum", - "thiserror", + "thiserror 1.0.69", "time", "tracing", "url", @@ -4232,9 +4770,9 @@ dependencies = [ [[package]] name = "sea-orm-cli" -version = "1.0.0-rc.1" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "096c0ffb155732ab98827d6a119c28166073933cb60e7662cc87f8b8af71ee31" +checksum = "7a8dbef29c7e534a8e9afb49abfa946c7a9df3d85f610dfed9140215ff8bff17" dependencies = [ "chrono", "clap", @@ -4249,23 +4787,23 @@ dependencies = [ [[package]] name = "sea-orm-macros" -version = "1.0.0-rc.1" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7be16d30795cc707c355d1c0ba704db085d5bd507a858509a0d784189b8fe31b" +checksum = "49ce6f08134f3681b1ca92185b96fac898f26d9b4f5538d13f7032ef243d14b2" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", "sea-bae", - "syn 2.0.50", + "syn 2.0.91", "unicode-ident", ] [[package]] name = "sea-orm-migration" -version = "1.0.0-rc.1" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4daac7104c6e919c4ae2da226b1ccb5d9b8c67046d85a597e20dee815d20319" +checksum = "2e53f46fe9874161ba57b15ff45d2589d754d7cbbbaab9470cb79cbada149ca7" dependencies = [ "async-trait", "clap", @@ -4280,13 +4818,12 @@ dependencies = [ [[package]] name = "sea-query" -version = "0.31.0-rc.4" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a3d4c4e985f331e06266751243c5ebe6f61e961dda605d71ea2e2b016ef478b" +checksum = "085e94f7d7271c0393ac2d164a39994b1dff1b06bc40cd9a0da04f3d672b0fee" dependencies = [ "bigdecimal", "chrono", - "derivative", "inherent", "ordered-float 3.9.2", "rust_decimal", @@ -4298,9 +4835,9 @@ dependencies = [ [[package]] name = "sea-query-binder" -version = "0.6.0-rc.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee39a8b40e6b4ee0510ee64c1079284559ee41f97919855b9cd7e43e1fb6ab04" +checksum = "b0019f47430f7995af63deda77e238c17323359af241233ec768aba1faea7608" dependencies = [ "bigdecimal", "chrono", @@ -4314,22 +4851,23 @@ dependencies = [ [[package]] name = "sea-query-derive" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25a82fcb49253abcb45cdcb2adf92956060ec0928635eb21b4f7a6d8f25ab0bc" +checksum = "9834af2c4bd8c5162f00c89f1701fb6886119a88062cf76fe842ea9e232b9839" dependencies = [ - "heck", + "darling", + "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.50", - "thiserror", + "syn 2.0.91", + "thiserror 1.0.69", ] [[package]] name = "sea-schema" -version = "0.15.0-rc.2" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a03b47e6cf73c34d83fff934c169732530f6f3b0dd072725804961959b7d1df" +checksum = "aab1592d17860a9a8584d9b549aebcd06f7bdc3ff615f71752486ba0b05b1e6e" dependencies = [ "futures", "sea-query", @@ -4338,14 +4876,14 @@ dependencies = [ [[package]] name = "sea-schema-derive" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6f686050f76bffc4f635cda8aea6df5548666b830b52387e8bc7de11056d11e" +checksum = "debdc8729c37fdbf88472f97fd470393089f997a909e535ff67c544d18cfccf0" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.91", ] [[package]] @@ -4356,11 +4894,11 @@ checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" [[package]] name = "security-framework" -version = "2.9.2" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", "core-foundation", "core-foundation-sys", "libc", @@ -4369,28 +4907,44 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.9.1" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +checksum = "1863fd3768cd83c56a7f60faa4dc0d403f1b6df0a38c3c25f44b7894e45370d5" dependencies = [ "core-foundation-sys", "libc", ] [[package]] -name = "semver" -version = "1.0.22" +name = "selectors" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" +checksum = "fd568a4c9bb598e291a08244a5c1f5a8a6650bee243b5b0f8dbb3d9cc1d87fe8" dependencies = [ - "serde", + "bitflags 2.6.0", + "cssparser 0.34.0", + "derive_more", + "fxhash", + "log", + "new_debug_unreachable", + "phf", + "phf_codegen", + "precomputed-hash", + "servo_arc", + "smallvec", ] [[package]] -name = "serde" -version = "1.0.197" +name = "semver" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba" + +[[package]] +name = "serde" +version = "1.0.216" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" dependencies = [ "serde_derive", ] @@ -4407,31 +4961,32 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.197" +version = "1.0.216" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.91", ] [[package]] name = "serde_json" -version = "1.0.114" +version = "1.0.134" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" +checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] [[package]] name = "serde_path_to_error" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebd154a240de39fdebcf5775d2675c204d7c13cf39a4c697be6493c8e734337c" +checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" dependencies = [ "itoa", "serde", @@ -4449,13 +5004,22 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.91", +] + +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", ] [[package]] @@ -4472,70 +5036,50 @@ dependencies = [ [[package]] name = "serde_variant" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47a8ec0b2fd0506290348d9699c0e3eb2e3e8c0498b5a9a6158b3bd4d6970076" +checksum = "0a0068df419f9d9b6488fdded3f1c818522cdea328e02ce9d9f147380265a432" dependencies = [ "serde", ] [[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 = [ - "serde", - "serde_with_macros 1.5.2", -] - -[[package]] -name = "serde_with" -version = "2.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07ff71d2c147a7b57362cead5e22f772cd52f6ab31cfcd9edcd7f6aeb2a0afbe" -dependencies = [ - "base64 0.13.1", + "base64 0.22.1", "chrono", "hex 0.4.3", "indexmap 1.9.3", + "indexmap 2.7.0", "serde", + "serde_derive", "serde_json", - "serde_with_macros 2.3.3", + "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", -] - -[[package]] -name = "serde_with_macros" -version = "2.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "881b6f881b17d13214e5d494c939ebab463d01264ce1811e9d4ac3a882e7695f" -dependencies = [ - "darling 0.20.8", - "proc-macro2", - "quote", - "syn 2.0.50", + "syn 2.0.91", ] [[package]] name = "serde_yaml" -version = "0.9.32" +version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd075d994154d4a774f95b51fb96bdc2832b0ea48425c92546073816cda1f2f" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.2.3", + "indexmap 2.7.0", "itoa", "ryu", "serde", @@ -4544,27 +5088,36 @@ dependencies = [ [[package]] name = "serial_test" -version = "2.0.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e56dd856803e253c8f298af3f4d7eb0ae5e23a737252cd90bb4f3b435033b2d" +checksum = "1b258109f244e1d6891bf1053a55d63a5cd4f8f4c30cf9a1280989f80e7a1fa9" dependencies = [ - "dashmap", "futures", - "lazy_static", "log", - "parking_lot", + "once_cell", + "parking_lot 0.12.3", + "scc", "serial_test_derive", ] [[package]] name = "serial_test_derive" -version = "2.0.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f" +checksum = "5d69265a08751de7844521fd15003ae0a888e035773ba05695c5c759a6f89eef" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.91", +] + +[[package]] +name = "servo_arc" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae65c4249478a2647db249fb43e23cec56a2c8974a427e7bd8cb5a1d0964921a" +dependencies = [ + "stable_deref_trait", ] [[package]] @@ -4580,9 +5133,9 @@ dependencies = [ [[package]] name = "sha1_smol" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" +checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" [[package]] name = "sha2" @@ -4606,12 +5159,12 @@ dependencies = [ [[package]] name = "shared_child" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0d94659ad3c2137fef23ae75b03d5241d633f8acded53d672decfa0e6e0caef" +checksum = "09fa9338aed9a1df411814a5b2252f7cd206c55ae9bf2fa763f8de84603aa60c" dependencies = [ "libc", - "winapi", + "windows-sys 0.59.0", ] [[package]] @@ -4621,31 +5174,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" [[package]] -name = "signal-hook" -version = "0.3.17" +name = "shlex" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" -dependencies = [ - "libc", - "signal-hook-registry", -] - -[[package]] -name = "signal-hook-mio" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" -dependencies = [ - "libc", - "mio", - "signal-hook", -] +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", ] @@ -4670,16 +5208,22 @@ dependencies = [ ] [[package]] -name = "simdutf8" -version = "0.1.4" +name = "simd-adler32" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" [[package]] name = "similar" -version = "2.4.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32fea41aca09ee824cc9724996433064c89f7777e60762749a4170a14abbfa21" +checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e" [[package]] name = "simple_asn1" @@ -4689,7 +5233,7 @@ checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" dependencies = [ "num-bigint", "num-traits", - "thiserror", + "thiserror 1.0.69", "time", ] @@ -4729,9 +5273,9 @@ dependencies = [ [[package]] name = "slug" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bd94acec9c8da640005f8e135a39fc0372e74535e6b368b7a04b875f784c8c4" +checksum = "882a80f72ee45de3cc9a5afeb2da0331d58df69e4e7d8eeb5d3c7784ae67e724" dependencies = [ "deunicode", "wasm-bindgen", @@ -4739,54 +5283,44 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" - -[[package]] -name = "smawk" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +dependencies = [ + "serde", +] [[package]] name = "snafu" -version = "0.7.5" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4de37ad025c587a29e8f3f5605c00f70b98715ef90b9061a815b9e59e9042d6" +checksum = "223891c85e2a29c3fe8fb900c1fae5e69c2e42415e3177752e8718475efa5019" dependencies = [ - "doc-comment", "snafu-derive", ] [[package]] name = "snafu-derive" -version = "0.7.5" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990079665f075b699031e9c08fd3ab99be5029b96f3b78dc0709e8f77e4efebf" +checksum = "03c3c6b7927ffe7ecaa769ee0e3994da3b8cafc8f444578982c83ecb161af917" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.91", ] [[package]] name = "socket2" -version = "0.5.6" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" dependencies = [ "libc", "windows-sys 0.52.0", ] -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - [[package]] name = "spin" version = "0.9.8" @@ -4808,20 +5342,19 @@ dependencies = [ [[package]] name = "sqlformat" -version = "0.2.3" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce81b7bd7c4493975347ef60d8c7e8b742d4694f4c49f93e0a12ea263938176c" +checksum = "7bba3a93db0cc4f7bdece8bb09e77e2e785c20bfebf79eb8340ed80708048790" dependencies = [ - "itertools 0.12.1", "nom", "unicode_categories", ] [[package]] name = "sqlx" -version = "0.7.3" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dba03c279da73694ef99763320dea58b51095dfe87d001b1d4b5fe78ba8763cf" +checksum = "93334716a037193fac19df402f8571269c84a00852f6a7066b5d2616dcd64d3e" dependencies = [ "sqlx-core", "sqlx-macros", @@ -4832,11 +5365,10 @@ dependencies = [ [[package]] name = "sqlx-core" -version = "0.7.3" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d84b0a3c3739e220d94b3239fd69fb1f74bc36e16643423bd99de3b43c21bfbd" +checksum = "d4d8060b456358185f7d50c55d9b5066ad956956fddec42ee2e8567134a8936e" dependencies = [ - "ahash 0.8.9", "atoi", "bigdecimal", "byteorder", @@ -4844,7 +5376,6 @@ dependencies = [ "chrono", "crc", "crossbeam-queue", - "dotenvy", "either", "event-listener", "futures-channel", @@ -4852,55 +5383,55 @@ dependencies = [ "futures-intrusive", "futures-io", "futures-util", + "hashbrown 0.14.5", "hashlink", "hex 0.4.3", - "indexmap 2.2.3", + "indexmap 2.7.0", "log", "memchr", "once_cell", "paste", "percent-encoding", "rust_decimal", - "rustls 0.21.10", - "rustls-pemfile 1.0.4", + "rustls", + "rustls-pemfile", "serde", "serde_json", "sha2", "smallvec", "sqlformat", - "thiserror", + "thiserror 1.0.69", "time", "tokio", "tokio-stream", "tracing", "url", "uuid", - "webpki-roots 0.25.4", + "webpki-roots", ] [[package]] name = "sqlx-macros" -version = "0.7.3" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89961c00dc4d7dffb7aee214964b065072bff69e36ddb9e2c107541f75e4f2a5" +checksum = "cac0692bcc9de3b073e8d747391827297e075c7710ff6276d9f7a1f3d58c6657" dependencies = [ "proc-macro2", "quote", "sqlx-core", "sqlx-macros-core", - "syn 1.0.109", + "syn 2.0.91", ] [[package]] name = "sqlx-macros-core" -version = "0.7.3" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0bd4519486723648186a08785143599760f7cc81c52334a55d6a83ea1e20841" +checksum = "1804e8a7c7865599c9c79be146dc8a9fd8cc86935fa641d3ea58e5f0688abaa5" dependencies = [ - "atomic-write-file", "dotenvy", "either", - "heck", + "heck 0.5.0", "hex 0.4.3", "once_cell", "proc-macro2", @@ -4912,7 +5443,7 @@ dependencies = [ "sqlx-mysql", "sqlx-postgres", "sqlx-sqlite", - "syn 1.0.109", + "syn 2.0.91", "tempfile", "tokio", "url", @@ -4920,14 +5451,14 @@ dependencies = [ [[package]] name = "sqlx-mysql" -version = "0.7.3" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e37195395df71fd068f6e2082247891bc11e3289624bbc776a0cdfa1ca7f1ea4" +checksum = "64bb4714269afa44aef2755150a0fc19d756fb580a67db8885608cf02f47d06a" dependencies = [ "atoi", - "base64 0.21.7", + "base64 0.22.1", "bigdecimal", - "bitflags 2.4.2", + "bitflags 2.6.0", "byteorder", "bytes", "chrono", @@ -4958,7 +5489,7 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror", + "thiserror 1.0.69", "time", "tracing", "uuid", @@ -4967,14 +5498,14 @@ dependencies = [ [[package]] name = "sqlx-postgres" -version = "0.7.3" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6ac0ac3b7ccd10cc96c7ab29791a7dd236bd94021f31eec7ba3d46a74aa1c24" +checksum = "6fa91a732d854c5d7726349bb4bb879bb9478993ceb764247660aee25f67c2f8" dependencies = [ "atoi", - "base64 0.21.7", + "base64 0.22.1", "bigdecimal", - "bitflags 2.4.2", + "bitflags 2.6.0", "byteorder", "chrono", "crc", @@ -4998,12 +5529,11 @@ dependencies = [ "rust_decimal", "serde", "serde_json", - "sha1", "sha2", "smallvec", "sqlx-core", "stringprep", - "thiserror", + "thiserror 1.0.69", "time", "tracing", "uuid", @@ -5012,9 +5542,9 @@ dependencies = [ [[package]] name = "sqlx-sqlite" -version = "0.7.3" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "210976b7d948c7ba9fced8ca835b11cbb2d677c59c79de41ac0d397e14547490" +checksum = "d5b2cf34a45953bfd3daaf3db0f7a7878ab9b7a6b91b422d24a7a9e4c857b680" dependencies = [ "atoi", "chrono", @@ -5028,25 +5558,31 @@ dependencies = [ "log", "percent-encoding", "serde", + "serde_urlencoded", "sqlx-core", "time", "tracing", "url", - "urlencoding", "uuid", ] [[package]] -name = "stacker" -version = "0.1.15" +name = "stable_deref_trait" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c886bd4480155fd3ef527d45e9ac8dd7118a898a46530b7b94c3e21866259fce" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "stacker" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799c883d55abdb5e98af1a7b3f23b9b6de8ecada0ecac058672d7635eb48ca7b" dependencies = [ "cc", "cfg-if", "libc", "psm", - "winapi", + "windows-sys 0.59.0", ] [[package]] @@ -5056,39 +5592,82 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] -name = "stringprep" -version = "0.1.4" +name = "string_cache" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb41d74e231a107a1b4ee36bd1214b11285b77768d2e3824aedafa988fd36ee6" +checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" +dependencies = [ + "new_debug_unreachable", + "once_cell", + "parking_lot 0.12.3", + "phf_shared 0.10.0", + "precomputed-hash", + "serde", +] + +[[package]] +name = "string_cache_codegen" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", + "proc-macro2", + "quote", +] + +[[package]] +name = "stringprep" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" dependencies = [ - "finl_unicode", "unicode-bidi", "unicode-normalization", + "unicode-properties", ] [[package]] name = "strsim" -version = "0.10.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] -name = "strsim" -version = "0.11.0" +name = "structmeta" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" +checksum = "2e1575d8d40908d70f6fd05537266b90ae71b15dbbe7a8b7dffa2b759306d329" +dependencies = [ + "proc-macro2", + "quote", + "structmeta-derive", + "syn 2.0.91", +] + +[[package]] +name = "structmeta-derive" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "152a0b65a590ff6c3da95cabe2353ee04e6167c896b28e3b14478c2636c922fc" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", +] [[package]] name = "strum" -version = "0.25.0" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" [[package]] name = "subtle" -version = "2.5.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" @@ -5103,9 +5682,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.50" +version = "2.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74f1bdc9872430ce9b75da68329d1c1746faf50ffac5f19e02b71e37ff881ffb" +checksum = "d53cbcb5a243bd33b7858b1d7f4aca2153490815872d86d955d6ea29f743c035" dependencies = [ "proc-macro2", "quote", @@ -5113,44 +5692,52 @@ dependencies = [ ] [[package]] -name = "syn_derive" -version = "0.1.8" +name = "sync_wrapper" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1329189c02ff984e9736652b1631330da25eaa6bc639089ed4915d25446cbe7b" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" dependencies = [ - "proc-macro-error", - "proc-macro2", - "quote", - "syn 2.0.50", + "futures-core", ] [[package]] -name = "sync_wrapper" -version = "0.1.2" +name = "synstructure" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", +] [[package]] name = "system-configuration" -version = "0.5.1" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", "core-foundation", "system-configuration-sys", ] [[package]] name = "system-configuration-sys" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" dependencies = [ "core-foundation-sys", "libc", ] +[[package]] +name = "tagptr" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417" + [[package]] name = "tap" version = "1.0.1" @@ -5159,21 +5746,33 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.10.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a365e8cd18e44762ef95d87f284f4b5cd04107fec2ff3052bd6a3e6069669e67" +checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" dependencies = [ "cfg-if", "fastrand", + "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.59.0", +] + +[[package]] +name = "tendril" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" +dependencies = [ + "futf", + "mac", + "utf-8", ] [[package]] name = "tera" -version = "1.19.1" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "970dff17c11e884a4a09bc76e3a17ef71e01bb13447a11e85226e254fe6d10b8" +checksum = "ab9d851b45e865f178319da0abdbfe6acbc4328759ff18dafc3a41c16b4cd2ee" dependencies = [ "chrono", "chrono-tz", @@ -5204,61 +5803,88 @@ dependencies = [ [[package]] name = "testcontainers" -version = "0.15.0" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f83d2931d7f521af5bae989f716c3fa43a6af9af7ec7a5e21b59ae40878cec00" +checksum = "5f40cc2bd72e17f328faf8ca7687fe337e61bccd8acf9674fa78dd3792b045e1" dependencies = [ + "async-trait", + "bollard", "bollard-stubs", + "bytes", + "docker_credential", + "either", + "etcetera", "futures", - "hex 0.4.3", - "hmac", "log", - "rand", + "memchr", + "parse-display", + "pin-project-lite", "serde", "serde_json", - "sha2", + "serde_with", + "thiserror 1.0.69", + "tokio", + "tokio-stream", + "tokio-tar", + "tokio-util", + "url", ] [[package]] name = "testcontainers-modules" -version = "0.3.5" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d0334776e1e8ee7c504a922c5236daf865ffe413aa630d84ae91dcce0b10bc3" +checksum = "064a2677e164cad39ef3c1abddb044d5a25c49d27005804563d8c4227aac8bd0" dependencies = [ "testcontainers", ] [[package]] -name = "textwrap" -version = "0.15.2" +name = "thiserror" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7b3e525a49ec206798b40326a44121291b530c963cfb01018f63e135bac543d" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "smawk", - "unicode-linebreak", - "unicode-width", + "thiserror-impl 1.0.69", ] [[package]] name = "thiserror" -version = "1.0.57" +version = "2.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" +checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc" dependencies = [ - "thiserror-impl", + "thiserror-impl 2.0.9", ] [[package]] name = "thiserror-impl" -version = "1.0.57" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.91", ] +[[package]] +name = "thiserror-impl" +version = "2.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "thousands" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bf63baf9f5039dadc247375c29eb13706706cfde997d0330d05aa63a77d8820" + [[package]] name = "thread_local" version = "1.1.8" @@ -5271,9 +5897,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.34" +version = "0.3.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" +checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" dependencies = [ "deranged", "itoa", @@ -5292,28 +5918,29 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.17" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" +checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" dependencies = [ "num-conv", "time-core", ] [[package]] -name = "tiny-keccak" -version = "2.0.2" +name = "tinystr" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" dependencies = [ - "crunchy", + "displaydoc", + "zerovec", ] [[package]] name = "tinyvec" -version = "1.6.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8" dependencies = [ "tinyvec_macros", ] @@ -5324,40 +5951,48 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" -[[package]] -name = "tl" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b130bd8a58c163224b44e217b4239ca7b927d82bf6cc2fea1fc561d15056e3f7" - [[package]] name = "tokio" -version = "1.36.0" +version = "1.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" +checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" dependencies = [ "backtrace", "bytes", "libc", "mio", - "num_cpus", - "parking_lot", + "parking_lot 0.12.3", "pin-project-lite", "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys 0.48.0", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-cron-scheduler" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2594dd7c2abbbafbb1c78d167fd10860dc7bd75f814cb051a1e0d3e796b9702" +dependencies = [ + "chrono", + "cron", + "num-derive", + "num-traits", + "tokio", + "tracing", + "uuid", ] [[package]] name = "tokio-macros" -version = "2.2.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.91", ] [[package]] @@ -5372,30 +6007,19 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.24.1" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" dependencies = [ - "rustls 0.21.10", - "tokio", -] - -[[package]] -name = "tokio-rustls" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" -dependencies = [ - "rustls 0.22.2", - "rustls-pki-types", + "rustls", "tokio", ] [[package]] name = "tokio-stream" -version = "0.1.14" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" dependencies = [ "futures-core", "pin-project-lite", @@ -5403,46 +6027,112 @@ dependencies = [ ] [[package]] -name = "tokio-util" -version = "0.7.10" +name = "tokio-tar" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +checksum = "9d5714c010ca3e5c27114c1cdeb9d14641ace49874aa5626d7149e47aedace75" +dependencies = [ + "filetime", + "futures-core", + "libc", + "redox_syscall 0.3.5", + "tokio", + "tokio-stream", + "xattr", +] + +[[package]] +name = "tokio-util" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" dependencies = [ "bytes", "futures-core", "futures-sink", "pin-project-lite", "tokio", - "tracing", +] + +[[package]] +name = "toml" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", ] [[package]] name = "toml_datetime" -version = "0.6.5" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] [[package]] name = "toml_edit" -version = "0.21.1" +version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ - "indexmap 2.2.3", + "indexmap 2.7.0", + "serde", + "serde_spanned", "toml_datetime", "winnow", ] +[[package]] +name = "torrent" +version = "0.1.0" +dependencies = [ + "async-trait", + "bytes", + "chrono", + "eyre", + "futures", + "itertools 0.13.0", + "lazy_static", + "librqbit-core", + "qbit-rs", + "quirks_path", + "regex", + "reqwest", + "serde", + "testcontainers", + "testcontainers-modules", + "thiserror 2.0.9", + "tokio", + "url", +] + [[package]] name = "tower" version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "tower-layer", + "tower-service", + "tracing", +] + +[[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", "pin-project-lite", + "sync_wrapper", "tokio", "tower-layer", "tower-service", @@ -5451,17 +6141,17 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.5.2" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" +checksum = "403fa3b783d4b626a8ad51d766ab03cb6d2dbfc46b1c5d4448395e6628dc9697" dependencies = [ "async-compression", - "bitflags 2.4.2", + "bitflags 2.6.0", "bytes", "futures-core", "futures-util", - "http 1.0.0", - "http-body 1.0.0", + "http 1.2.0", + "http-body", "http-body-util", "http-range-header", "httpdate", @@ -5478,21 +6168,21 @@ dependencies = [ [[package]] name = "tower-layer" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" [[package]] name = "tower-service" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +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", @@ -5501,21 +6191,33 @@ dependencies = [ ] [[package]] -name = "tracing-attributes" -version = "0.1.27" +name = "tracing-appender" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf" +dependencies = [ + "crossbeam-channel", + "thiserror 1.0.69", + "time", + "tracing-subscriber", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.91", ] [[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", "valuable", @@ -5534,9 +6236,9 @@ dependencies = [ [[package]] name = "tracing-serde" -version = "0.1.3" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" +checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1" dependencies = [ "serde", "tracing-core", @@ -5544,9 +6246,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.18" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" dependencies = [ "matchers", "nu-ansi-term", @@ -5563,6 +6265,12 @@ dependencies = [ "tracing-serde", ] +[[package]] +name = "triomphe" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "859eb650cfee7434994602c3a68b25d77ad9e68c8a6cd491616ef86661382eb3" + [[package]] name = "try-lock" version = "0.2.5" @@ -5571,13 +6279,22 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "typed-builder" -version = "0.14.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64cba322cb9b7bc6ca048de49e83918223f35e7a86311267013afff257004870" +checksum = "7e14ed59dc8b7b26cacb2a92bad2e8b1f098806063898ab42a3bd121d7d45e75" +dependencies = [ + "typed-builder-macro", +] + +[[package]] +name = "typed-builder-macro" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "560b82d656506509d43abe30e0ba64c56b1953ab3d4fe7ba5902747a7a3cedd5" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.91", ] [[package]] @@ -5588,9 +6305,20 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "ucd-trie" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + +[[package]] +name = "ulid" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04f903f293d11f31c0c29e4148f6dc0d033a7f80cebc0282bea147611667d289" +dependencies = [ + "getrandom", + "rand", + "web-time", +] [[package]] name = "unic-char-property" @@ -5644,51 +6372,54 @@ dependencies = [ [[package]] name = "unicase" -version = "2.7.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" -dependencies = [ - "version_check", -] +checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df" [[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.12" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "unicode-linebreak" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "unicode-normalization" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" dependencies = [ "tinyvec", ] [[package]] -name = "unicode-segmentation" -version = "1.11.0" +name = "unicode-properties" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" +checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "unicode-width" -version = "0.1.11" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "unicode-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" [[package]] name = "unicode_categories" @@ -5698,9 +6429,9 @@ checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" [[package]] name = "unsafe-libyaml" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" [[package]] name = "untrusted" @@ -5710,21 +6441,27 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.0" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", - "idna 0.5.0", + "idna 1.0.3", "percent-encoding", "serde", ] [[package]] -name = "urlencoding" -version = "2.1.3" +name = "utf-8" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" [[package]] name = "utf8-width" @@ -5733,29 +6470,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3" [[package]] -name = "utf8parse" -version = "0.2.1" +name = "utf8_iter" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.7.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a" +checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" dependencies = [ "getrandom", + "rand", "serde", ] [[package]] name = "validator" -version = "0.16.1" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b92f40481c04ff1f4f61f304d61793c7b56ff76ac1469f1beb199b1445b253bd" +checksum = "db79c75af171630a3148bd3e6d7c4f42b6a9a014c2945bc5ed0020cbb8d9478e" dependencies = [ - "idna 0.4.0", - "lazy_static", + "idna 0.5.0", + "once_cell", "regex", "serde", "serde_derive", @@ -5765,29 +6509,32 @@ dependencies = [ ] [[package]] -name = "validator_derive" -version = "0.16.0" +name = "validator" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc44ca3088bb3ba384d9aecf40c6a23a676ce23e09bdaca2073d99c207f864af" +checksum = "d0b4a29d8709210980a09379f27ee31549b73292c87ab9899beee1c0d3be6303" dependencies = [ - "if_chain", - "lazy_static", - "proc-macro-error", - "proc-macro2", - "quote", + "idna 1.0.3", + "once_cell", "regex", - "syn 1.0.109", - "validator_types", + "serde", + "serde_derive", + "serde_json", + "url", ] [[package]] -name = "validator_types" -version = "0.16.0" +name = "validator_derive" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "111abfe30072511849c5910134e8baf8dc05de4c0e5903d681cbd5c9c4d611e3" +checksum = "df0bcf92720c40105ac4b2dda2a4ea3aa717d4d6a862cc217da653a4bd5c6b10" dependencies = [ + "darling", + "once_cell", + "proc-macro-error", "proc-macro2", - "syn 1.0.109", + "quote", + "syn 2.0.91", ] [[package]] @@ -5804,9 +6551,9 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "vlq" @@ -5816,9 +6563,9 @@ checksum = "65dd7eed29412da847b0f78bcec0ac98588165988a8cfe41d4ea1d429f8ccfff" [[package]] name = "walkdir" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", @@ -5840,47 +6587,54 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] -name = "wasm-bindgen" -version = "0.2.91" +name = "wasite" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f" +checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" dependencies = [ "cfg-if", + "once_cell", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.91" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b" +checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.91", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.41" +version = "0.4.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877b9c3f61ceea0e56331985743b13f3d25c406a7098d45180fb5f09bc19ed97" +checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2" dependencies = [ "cfg-if", "js-sys", + "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.91" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed" +checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -5888,28 +6642,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.91" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" +checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.91", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.91" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838" +checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" [[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", @@ -5919,10 +6673,35 @@ dependencies = [ ] [[package]] -name = "web-sys" -version = "0.3.68" +name = "wasm-timer" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96565907687f7aceb35bc5fc03770a8a0471d82e479f25832f54a0e3f4b28446" +checksum = "be0ecb0db480561e9a7642b5d3e4187c128914e58aa84330b9493e3eb68c5e7f" +dependencies = [ + "futures", + "js-sys", + "parking_lot 0.11.2", + "pin-utils", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "web-sys" +version = "0.3.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" dependencies = [ "js-sys", "wasm-bindgen", @@ -5930,24 +6709,22 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.25.4" +version = "0.26.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" - -[[package]] -name = "webpki-roots" -version = "0.26.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3de34ae270483955a94f4b21bdaaeb83d508bb84a01435f393818edb0012009" +checksum = "5d642ff16b7e79272ae451b7322067cdc17cadf68c23264be9d94a32319efe7e" dependencies = [ "rustls-pki-types", ] [[package]] name = "whoami" -version = "1.4.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22fc3756b8a9133049b26c7f61ab35416c130e8c09b660f5b3958b446f52cc50" +checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d" +dependencies = [ + "redox_syscall 0.5.8", + "wasite", +] [[package]] name = "winapi" @@ -5967,11 +6744,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.6" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "winapi", + "windows-sys 0.59.0", ] [[package]] @@ -5980,13 +6757,53 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core", + "windows-targets 0.52.6", +] + [[package]] name = "windows-core" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.3", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-registry" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +dependencies = [ + "windows-result", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets 0.52.6", ] [[package]] @@ -6004,7 +6821,16 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.3", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", ] [[package]] @@ -6024,17 +6850,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.3" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d380ba1dc7187569a8a9e91ed34b8ccfc33123bbacb8c0aed2d1ad7f3ef2dc5f" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.3", - "windows_aarch64_msvc 0.52.3", - "windows_i686_gnu 0.52.3", - "windows_i686_msvc 0.52.3", - "windows_x86_64_gnu 0.52.3", - "windows_x86_64_gnullvm 0.52.3", - "windows_x86_64_msvc 0.52.3", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "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]] @@ -6045,9 +6872,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.3" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68e5dcfb9413f53afd9c8f86e56a7b4d86d9a2fa26090ea2dc9e40fba56c6ec6" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" @@ -6057,9 +6884,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.3" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dab469ebbc45798319e69eebf92308e541ce46760b49b18c6b3fe5e8965b30f" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" @@ -6069,9 +6896,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.3" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a4e9b6a7cac734a8b4138a4e1044eac3404d8326b6c0f939276560687a033fb" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" @@ -6081,9 +6914,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.3" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b0ec9c422ca95ff34a78755cfa6ad4a51371da2a5ace67500cf7ca5f232c58" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" @@ -6093,9 +6926,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.3" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "704131571ba93e89d7cd43482277d6632589b18ecf4468f591fbae0a8b101614" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" @@ -6105,9 +6938,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.3" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42079295511643151e98d61c38c0acc444e52dd42ab456f7ccfd5152e8ecf21c" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" @@ -6117,34 +6950,30 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.3" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0770833d60a970638e989b3fa9fd2bb1aaadcf88963d1659fd7d9990196ed2d6" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.5.40" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" dependencies = [ "memchr", ] [[package]] -name = "winreg" -version = "0.50.0" +name = "write16" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" [[package]] -name = "winsplit" -version = "0.1.0" +name = "writeable" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ab703352da6a72f35c39a533526393725640575bb211f61987a2748323ad956" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" [[package]] name = "wyz" @@ -6156,70 +6985,265 @@ dependencies = [ ] [[package]] -name = "yaml-rust" -version = "0.4.5" +name = "xattr" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f" dependencies = [ - "linked-hash-map", + "libc", + "linux-raw-sys", + "rustix", ] [[package]] name = "yansi" -version = "0.5.1" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + +[[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 2.0.91", + "synstructure", +] [[package]] name = "zerocopy" -version = "0.7.32" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ + "byteorder", "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.32" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.91", +] + +[[package]] +name = "zerofrom" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", + "synstructure", ] [[package]] name = "zeroize" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +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 2.0.91", +] [[package]] name = "zstd" -version = "0.13.0" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bffb3309596d527cfcba7dfc6ed6052f1d39dfbd7c867aa2e865e4a449c10110" +checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9" dependencies = [ "zstd-safe", ] [[package]] name = "zstd-safe" -version = "7.0.0" +version = "7.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43747c7422e2924c11144d5229878b98180ef8b06cca4ab5af37afc8a8d8ea3e" +checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059" dependencies = [ "zstd-sys", ] [[package]] name = "zstd-sys" -version = "2.0.9+zstd.1.5.5" +version = "2.0.13+zstd.1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" +checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" dependencies = [ "cc", "pkg-config", ] + +[[package]] +name = "zune-bmp" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b1d6d8d84344ba202fcf02d6505ab57c2d775ae07dcb64809421735d8ce8c0f" +dependencies = [ + "zune-core", +] + +[[package]] +name = "zune-core" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" +dependencies = [ + "log", + "serde", +] + +[[package]] +name = "zune-farbfeld" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eea864ca80549c562044a8632421897a1e419ca460c20bd07fe84371f410f011" +dependencies = [ + "zune-core", +] + +[[package]] +name = "zune-hdr" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51ff474631f80b14afc6dbc6bab78702ed2de7cd6af85f7a4ab21d3b08686426" +dependencies = [ + "zune-core", +] + +[[package]] +name = "zune-image" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6b98ac0fc8c650406e88f7b33f264798b85b7eff88cc09a8f86688c24d0261" +dependencies = [ + "bytemuck", + "jpeg-encoder", + "jxl-oxide", + "kamadak-exif", + "serde", + "zune-bmp", + "zune-core", + "zune-farbfeld", + "zune-hdr", + "zune-jpeg", + "zune-jpegxl", + "zune-png", + "zune-ppm", + "zune-psd", + "zune-qoi", +] + +[[package]] +name = "zune-inflate" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "zune-jpeg" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99a5bab8d7dedf81405c4bb1f2b83ea057643d9cb28778cea9eecddeedd2e028" +dependencies = [ + "zune-core", +] + +[[package]] +name = "zune-jpegxl" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71ffee484384b15f99ed4768bfdb3fa186d63e1f8c3aafba1d6d141a7b9e3674" +dependencies = [ + "zune-core", +] + +[[package]] +name = "zune-png" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d29c085769c6f29effea890f093120ac019375fdc789d2a496ba8ba96c77509" +dependencies = [ + "zune-core", + "zune-inflate", +] + +[[package]] +name = "zune-ppm" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0527e645233e3b34b10aa576b4b156e60f52f15b01f906789eb820f7ef4b2774" +dependencies = [ + "log", + "zune-core", +] + +[[package]] +name = "zune-psd" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab11275d621813206ba4c20f4ec647eab0733c0fc71c3276d1238229fd834d49" +dependencies = [ + "zune-core", +] + +[[package]] +name = "zune-qoi" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc598b6729ede546413cb5077d8ddb83b46055a156d5cc3c625cfe122484e9c9" +dependencies = [ + "zune-core", +] diff --git a/Cargo.toml b/Cargo.toml index a68c36a..23eaeac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,3 @@ [workspace] -members = ["crates/quirks_path", "crates/recorder"] +members = ["crates/quirks-path", "crates/recorder", "crates/torrent"] resolver = "2" diff --git a/config/recorder.development.yaml b/config/recorder.development.yaml index 204a422..2c83557 100644 --- a/config/recorder.development.yaml +++ b/config/recorder.development.yaml @@ -88,7 +88,7 @@ database: # Database connection URI uri: '{{ get_env(name="DATABASE_URL", default="postgres://konobangu:konobangu@127.0.0.1:5432/konobangu") }}' # When enabled, the sql query will be logged. - enable_logging: false + enable_logging: true # Set the timeout duration when acquiring a connection. connect_timeout: 500 # Set the idle duration before closing a connection. @@ -111,3 +111,6 @@ redis: # Dangerously flush all data in Redis on startup. dangerous operation, make sure that you using this flag only on dev environments or test mode dangerously_flush: false +settings: + dal: + fs_root: ./temp \ No newline at end of file diff --git a/crates/quirks_path/Cargo.toml b/crates/quirks-path/Cargo.toml similarity index 100% rename from crates/quirks_path/Cargo.toml rename to crates/quirks-path/Cargo.toml diff --git a/crates/quirks_path/src/lib.rs b/crates/quirks-path/src/lib.rs similarity index 99% rename from crates/quirks_path/src/lib.rs rename to crates/quirks-path/src/lib.rs index 879b269..249ff4c 100644 --- a/crates/quirks_path/src/lib.rs +++ b/crates/quirks-path/src/lib.rs @@ -1,4 +1,3 @@ -#![feature(strict_provenance)] #![feature(extend_one)] mod url; @@ -605,10 +604,9 @@ impl<'a> PartialEq for Components<'a> { && self.back == State::Body && other.back == State::Body && self.prefix_verbatim() == other.prefix_verbatim() + && self.path == other.path { - if self.path == other.path { - return true; - } + return true; } Iterator::eq(self.clone().rev(), other.clone().rev()) @@ -714,10 +712,11 @@ impl PathBuf { fn _push(&mut self, path: &Path) { let main_sep_str = self.get_main_sep(); + let mut need_sep = self .as_mut_vec() .last() - .map_or(false, |c| !is_separator(*c as char)); + .is_some_and(|c| !is_separator(*c as char)); let comps = self.components(); @@ -772,11 +771,11 @@ impl PathBuf { self.inner.push(main_sep_str) } - self.inner.extend(path) + self.inner.push_str(path.as_str()) } pub fn pop(&mut self) -> bool { - match self.parent().map(|p| p.inner.as_bytes().len()) { + match self.parent().map(|p| p.inner.len()) { Some(len) => { self.as_mut_vec().truncate(len); true @@ -1121,7 +1120,7 @@ impl Eq for PathBuf {} impl PartialOrd for PathBuf { #[inline] fn partial_cmp(&self, other: &PathBuf) -> Option { - Some(compare_components(self.components(), other.components())) + Some(self.cmp(other)) } } @@ -1199,7 +1198,7 @@ impl Path { } pub fn ancestors(&self) -> Ancestors<'_> { - Ancestors { next: Some(&self) } + Ancestors { next: Some(self) } } pub fn file_name(&self) -> Option<&str> { @@ -1247,7 +1246,7 @@ impl Path { pub fn file_prefix(&self) -> Option<&str> { self.file_name() .map(split_file_at_dot) - .and_then(|(before, _after)| Some(before)) + .map(|(before, _after)| before) } pub fn extension(&self) -> Option<&str> { diff --git a/crates/quirks_path/src/url.rs b/crates/quirks-path/src/url.rs similarity index 100% rename from crates/quirks_path/src/url.rs rename to crates/quirks-path/src/url.rs diff --git a/crates/quirks_path/src/windows.rs b/crates/quirks-path/src/windows.rs similarity index 100% rename from crates/quirks_path/src/windows.rs rename to crates/quirks-path/src/windows.rs diff --git a/crates/recorder/Cargo.toml b/crates/recorder/Cargo.toml index 18c9d45..21b8866 100644 --- a/crates/recorder/Cargo.toml +++ b/crates/recorder/Cargo.toml @@ -13,57 +13,57 @@ name = "recorder_cli" path = "src/bin/main.rs" required-features = [] -[features] -default = [] -testcontainers = [] - [dependencies] -loco-rs = { version = "0.3.1" } +loco-rs = { version = "0.13.2" } serde = { version = "1", features = ["derive"] } serde_json = "1" eyre = "0.6" -tokio = { version = "1.33.0", default-features = false } -async-trait = "0.1.74" -tracing = "0.1.40" +tokio = { version = "1.42", default-features = false } +async-trait = "0.1.83" +tracing = "0.1.41" chrono = "0.4" -validator = { version = "0.16" } -sea-orm = { version = "1.0.0-rc.1", features = [ +validator = { version = "0.19" } +sea-orm = { version = "1.1.3", features = [ "sqlx-sqlite", "sqlx-postgres", "runtime-tokio-rustls", "macros", ] } -axum = "0.7.1" +axum = "0.7.9" include_dir = "0.7" uuid = { version = "1.6.0", features = ["v4"] } -tracing-subscriber = { version = "0.3.17", features = ["env-filter", "json"] } -sea-orm-migration = { version = "1.0.0-rc.1", features = [ - "runtime-tokio-rustls", -] } -reqwest = "0.11.24" -thiserror = "1.0.57" -rss = "2.0.7" -bytes = "1.5.0" -futures = "0.3.30" -itertools = "0.12.1" -qbit-rs = { git = "https://github.com/George-Miao/qbit.git", rev = "ad5af6a", features = ["default", "builder"] } -url = "2.5.0" -fancy-regex = "0.13.0" -regex = "1.10.3" -lazy_static = "1.4.0" +tracing-subscriber = { version = "0.3.19", features = ["env-filter", "json"] } +sea-orm-migration = { version = "1.1.3", features = ["runtime-tokio-rustls"] } +reqwest = "0.12.9" +thiserror = "2" +rss = "2" +bytes = "1.9" +futures = "0.3.31" +itertools = "0.13.0" +url = "2.5" +fancy-regex = "0.14" +regex = "1.11" +lazy_static = "1.5" maplit = "1.0.2" -tl = { version = "0.7.8", features = ["simd"] } -lightningcss = "1.0.0-alpha.54" +lightningcss = "1.0.0-alpha.61" html-escape = "0.2.13" -opendal = "0.45.0" -librqbit-core = "3.5.0" -quirks_path = { path = "../quirks_path" } +opendal = { version = "0.51.0", features = ["default", "services-fs"] } +quirks_path = { path = "../quirks-path" } +torrent = { path = "../torrent" } +zune-image = "0.4.15" +once_cell = "1.20.2" +reqwest-middleware = "0.4.0" +reqwest-retry = "0.7.0" +reqwest-tracing = "0.5.5" +scraper = "0.22.0" +leaky-bucket = "1.1.2" +serde_with = "3" [dev-dependencies] -serial_test = "2.0.0" -rstest = "0.18.2" -loco-rs = { version = "0.3.1", features = ["testing"] } -insta = { version = "1.34.0", features = ["redactions", "yaml", "filters"] } -testcontainers = { version = "0.15.0" } -testcontainers-modules = { version = "0.3.5" } +serial_test = "3" +rstest = "0.23.0" +loco-rs = { version = "0.13.2", features = ["testing"] } +insta = { version = "1", features = ["redactions", "yaml", "filters"] } +testcontainers = { version = "0.23.1" } +testcontainers-modules = { version = "0.11.4" } diff --git a/crates/recorder/examples/playground.rs b/crates/recorder/examples/playground.rs index 4f8813d..34f784b 100644 --- a/crates/recorder/examples/playground.rs +++ b/crates/recorder/examples/playground.rs @@ -1,20 +1,62 @@ #![allow(unused_imports)] use eyre::Context; -use loco_rs::{cli::playground, prelude::*}; +use itertools::Itertools; +use loco_rs::{ + app::Hooks, + boot::{BootResult, StartMode}, + environment::Environment, + prelude::*, +}; +use recorder::{ + app::App, + extract::mikan::parse_mikan_rss_items_from_rss_link, + migrations::Migrator, + models::{ + subscribers::ROOT_SUBSCRIBER, + subscriptions::{self, SubscriptionCreateFromRssDto}, + }, +}; +use sea_orm_migration::MigratorTrait; -async fn fetch_and_parse_rss_demo() -> eyre::Result<()> { - let url = - "https://mikanani.me/RSS/MyBangumi?token=FE9tccsML2nBPUUqpCuJW2uJZydAXCntHJ7RpD9LDP8%3d"; +async fn pull_mikan_bangumi_rss(ctx: &AppContext) -> eyre::Result<()> { + let rss_link = "https://mikanani.me/RSS/Bangumi?bangumiId=3416&subgroupid=370"; + let subscription = if let Some(subscription) = subscriptions::Entity::find() + .filter(subscriptions::Column::SourceUrl.eq(String::from(rss_link))) + .one(&ctx.db) + .await? + { + subscription + } else { + subscriptions::Model::add_subscription( + ctx, + subscriptions::SubscriptionCreateDto::Mikan(SubscriptionCreateFromRssDto { + rss_link: rss_link.to_string(), + display_name: String::from("Mikan Project - 我的番组"), + enabled: Some(true), + }), + 1, + ) + .await? + }; + + subscription.pull_subscription(ctx).await?; - let res = reqwest::get(url).await?.bytes().await?; - let channel = rss::Channel::read_from(&res[..])?; - println!("channel: {:#?}", channel); Ok(()) } +async fn init() -> eyre::Result { + let ctx = loco_rs::cli::playground::().await?; + let BootResult { + app_context: ctx, .. + } = loco_rs::boot::run_app::(&StartMode::ServerOnly, ctx).await?; + Migrator::up(&ctx.db, None).await?; + Ok(ctx) +} + #[tokio::main] async fn main() -> eyre::Result<()> { - fetch_and_parse_rss_demo().await?; + let ctx = init().await?; + pull_mikan_bangumi_rss(&ctx).await?; // let active_model: articles::ActiveModel = ActiveModel { // title: Set(Some("how to build apps in 3 steps".to_string())), @@ -25,7 +67,6 @@ async fn main() -> eyre::Result<()> { // let res = articles::Entity::find().all(&ctx.db).await.unwrap(); // println!("{:?}", res); - println!("welcome to playground. edit me at `examples/playground.rs`"); Ok(()) } diff --git a/crates/recorder/src/app.rs b/crates/recorder/src/app.rs index 970a823..805964d 100644 --- a/crates/recorder/src/app.rs +++ b/crates/recorder/src/app.rs @@ -4,20 +4,37 @@ use async_trait::async_trait; use loco_rs::{ app::{AppContext, Hooks}, boot::{create_app, BootResult, StartMode}, + cache, controller::AppRoutes, db::truncate_table, environment::Environment, + prelude::*, task::Tasks, - worker::{AppWorker, Processor}, Result, }; use sea_orm::DatabaseConnection; use crate::{ - controllers, migrations::Migrator, models::entities::subscribers, + controllers, + dal::{AppDalClient, AppDalInitalizer}, + extract::mikan::{client::AppMikanClientInitializer, AppMikanClient}, + migrations::Migrator, + models::entities::subscribers, workers::subscription_worker::SubscriptionWorker, }; +pub trait AppContextExt { + fn get_dal_client(&self) -> &AppDalClient { + AppDalClient::global() + } + + fn get_mikan_client(&self) -> &AppMikanClient { + AppMikanClient::global() + } +} + +impl AppContextExt for AppContext {} + pub struct App; #[async_trait] @@ -26,6 +43,15 @@ impl Hooks for App { env!("CARGO_CRATE_NAME") } + async fn initializers(_ctx: &AppContext) -> Result>> { + let initializers: Vec> = vec![ + Box::new(AppDalInitalizer), + Box::new(AppMikanClientInitializer), + ]; + + Ok(initializers) + } + fn app_version() -> String { format!( "{} ({})", @@ -46,8 +72,16 @@ impl Hooks for App { .add_route(controllers::subscribers::routes()) } - fn connect_workers<'a>(p: &'a mut Processor, ctx: &'a AppContext) { - p.register(SubscriptionWorker::build(ctx)); + async fn connect_workers(ctx: &AppContext, queue: &Queue) -> Result<()> { + queue.register(SubscriptionWorker::build(ctx)).await?; + Ok(()) + } + + async fn after_context(ctx: AppContext) -> Result { + Ok(AppContext { + cache: cache::Cache::new(cache::drivers::inmem::new()).into(), + ..ctx + }) } fn register_tasks(_tasks: &mut Tasks) {} diff --git a/crates/recorder/src/bin/main.rs b/crates/recorder/src/bin/main.rs index 33f9349..5983ff0 100644 --- a/crates/recorder/src/bin/main.rs +++ b/crates/recorder/src/bin/main.rs @@ -1,8 +1,8 @@ use loco_rs::cli; -use recorder::migrations::Migrator; -use recorder::app::App; +use recorder::{app::App, migrations::Migrator}; #[tokio::main] async fn main() -> eyre::Result<()> { - cli::main::().await + cli::main::().await?; + Ok(()) } diff --git a/crates/recorder/src/config/dal_conf.rs b/crates/recorder/src/config/dal_conf.rs deleted file mode 100644 index 4ea5c86..0000000 --- a/crates/recorder/src/config/dal_conf.rs +++ /dev/null @@ -1,10 +0,0 @@ -use serde::{Deserialize, Serialize}; - -pub fn default_app_dal_fs_root() -> String { - String::from("data") -} - -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct AppDalConf { - pub fs_root: String, -} diff --git a/crates/recorder/src/config/mod.rs b/crates/recorder/src/config/mod.rs index de881d8..8457f1f 100644 --- a/crates/recorder/src/config/mod.rs +++ b/crates/recorder/src/config/mod.rs @@ -1,44 +1,53 @@ -pub mod dal_conf; -pub use dal_conf::AppDalConf; -use eyre::OptionExt; -use itertools::Itertools; -use serde::{de::DeserializeOwned, Deserialize, Serialize}; +use serde::de::DeserializeOwned; -pub const DAL_CONF_KEY: &str = "dal"; - -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct AppCustomConf { - pub dal: AppDalConf, -} +use crate::{ + dal::{config::AppDalConfig, DAL_CONF_KEY}, + extract::mikan::{AppMikanConfig, MIKAN_CONF_KEY}, +}; pub fn deserialize_key_path_from_json_value( - key_path: &[&str], value: &serde_json::Value, -) -> eyre::Result { + key_path: &[&str], +) -> Result, loco_rs::Error> { let mut stack = vec![("", value)]; for key in key_path { let current = stack.last().unwrap().1; if let Some(v) = current.get(key) { stack.push((key, v)); } else { - let failed_key_path = stack.iter().map(|s| s.0).collect_vec().join("."); - return Err(eyre::eyre!( - "can not config key {} of settings", - failed_key_path - )); + return Ok(None); } } let result: T = serde_json::from_value(stack.pop().unwrap().1.clone())?; - Ok(result) + Ok(Some(result)) } -pub fn deserialize_key_path_from_loco_rs_config( - key_path: &[&str], +pub fn deserialize_key_path_from_app_config( app_config: &loco_rs::config::Config, -) -> eyre::Result { - let settings = app_config - .settings - .as_ref() - .ok_or_eyre("App config setting not set")?; - deserialize_key_path_from_json_value(key_path, settings) + key_path: &[&str], +) -> Result, loco_rs::Error> { + let settings = app_config.settings.as_ref(); + if let Some(settings) = settings { + deserialize_key_path_from_json_value(settings, key_path) + } else { + Ok(None) + } +} + +pub trait AppConfigExt { + fn get_root_conf(&self) -> &loco_rs::config::Config; + + fn get_dal_conf(&self) -> loco_rs::Result> { + deserialize_key_path_from_app_config(self.get_root_conf(), &[DAL_CONF_KEY]) + } + + fn get_mikan_conf(&self) -> loco_rs::Result> { + deserialize_key_path_from_app_config(self.get_root_conf(), &[MIKAN_CONF_KEY]) + } +} + +impl AppConfigExt for loco_rs::config::Config { + fn get_root_conf(&self) -> &loco_rs::config::Config { + self + } } diff --git a/crates/recorder/src/controllers/subscribers.rs b/crates/recorder/src/controllers/subscribers.rs index bfbda03..2dbddbb 100644 --- a/crates/recorder/src/controllers/subscribers.rs +++ b/crates/recorder/src/controllers/subscribers.rs @@ -2,8 +2,8 @@ use loco_rs::prelude::*; use crate::{models::entities::subscribers, views::subscribers::CurrentResponse}; -async fn current(State(ctx): State) -> Result> { - let subscriber = subscribers::Model::find_root(&ctx.db).await?; +async fn current(State(ctx): State) -> Result { + let subscriber = subscribers::Model::find_root(&ctx).await?; format::json(CurrentResponse::new(&subscriber)) } diff --git a/crates/recorder/src/dal/client.rs b/crates/recorder/src/dal/client.rs new file mode 100644 index 0000000..8637e5e --- /dev/null +++ b/crates/recorder/src/dal/client.rs @@ -0,0 +1,201 @@ +use std::fmt; + +use bytes::Bytes; +use loco_rs::app::{AppContext, Initializer}; +use once_cell::sync::OnceCell; +use opendal::{layers::LoggingLayer, services::Fs, Buffer, Operator}; +use quirks_path::{Path, PathBuf}; +use serde::{Deserialize, Serialize}; +use url::Url; +use uuid::Uuid; + +use super::AppDalConfig; +use crate::config::AppConfigExt; + +// TODO: wait app-context-trait to integrate +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum DalContentCategory { + Image, +} + +impl AsRef for DalContentCategory { + fn as_ref(&self) -> &str { + match self { + Self::Image => "image", + } + } +} + +#[derive(Debug, Clone)] +pub struct AppDalClient { + pub config: AppDalConfig, +} + +static APP_DAL_CLIENT: OnceCell = OnceCell::new(); + +pub enum DalStoredUrl { + RelativePath { path: String }, + Absolute { url: Url }, +} + +impl AsRef for DalStoredUrl { + fn as_ref(&self) -> &str { + match &self { + Self::Absolute { url } => url.as_str(), + Self::RelativePath { path } => path, + } + } +} + +impl fmt::Display for DalStoredUrl { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.as_ref()) + } +} + +impl AppDalClient { + pub fn new(config: AppDalConfig) -> Self { + Self { config } + } + + pub fn global() -> &'static AppDalClient { + APP_DAL_CLIENT + .get() + .expect("Global app dal client is not initialized") + } + + pub fn get_fs(&self) -> Fs { + Fs::default().root( + self.config + .data_dir + .as_ref() + .map(|s| s as &str) + .unwrap_or("data"), + ) + } + + pub fn create_filename(extname: &str) -> String { + format!("{}{}", Uuid::new_v4(), extname) + } + + pub async fn store_object( + &self, + content_category: DalContentCategory, + subscriber_pid: &str, + bucket: Option<&str>, + filename: &str, + data: Bytes, + ) -> eyre::Result { + match content_category { + DalContentCategory::Image => { + let fullname = [ + subscriber_pid, + content_category.as_ref(), + bucket.unwrap_or_default(), + filename, + ] + .into_iter() + .map(Path::new) + .collect::(); + + let fs_op = Operator::new(self.get_fs())? + .layer(LoggingLayer::default()) + .finish(); + + if let Some(dirname) = fullname.parent() { + let dirname = dirname.join("/"); + fs_op.create_dir(dirname.as_str()).await?; + } + + fs_op.write(fullname.as_str(), data).await?; + + Ok(DalStoredUrl::RelativePath { + path: fullname.to_string(), + }) + } + } + } + + pub async fn exists_object( + &self, + content_category: DalContentCategory, + subscriber_pid: &str, + bucket: Option<&str>, + filename: &str, + ) -> eyre::Result> { + match content_category { + DalContentCategory::Image => { + let fullname = [ + subscriber_pid, + content_category.as_ref(), + bucket.unwrap_or_default(), + filename, + ] + .into_iter() + .map(Path::new) + .collect::(); + + let fs_op = Operator::new(self.get_fs())? + .layer(LoggingLayer::default()) + .finish(); + + if fs_op.exists(fullname.as_str()).await? { + Ok(Some(DalStoredUrl::RelativePath { + path: fullname.to_string(), + })) + } else { + Ok(None) + } + } + } + } + + pub async fn load_object( + &self, + content_category: DalContentCategory, + subscriber_pid: &str, + bucket: Option<&str>, + filename: &str, + ) -> eyre::Result { + match content_category { + DalContentCategory::Image => { + let fullname = [ + subscriber_pid, + content_category.as_ref(), + bucket.unwrap_or_default(), + filename, + ] + .into_iter() + .map(Path::new) + .collect::(); + + let fs_op = Operator::new(self.get_fs())? + .layer(LoggingLayer::default()) + .finish(); + + let data = fs_op.read(fullname.as_str()).await?; + + Ok(data) + } + } + } +} + +pub struct AppDalInitalizer; + +#[async_trait::async_trait] +impl Initializer for AppDalInitalizer { + fn name(&self) -> String { + String::from("AppDalInitalizer") + } + + async fn before_run(&self, app_context: &AppContext) -> loco_rs::Result<()> { + let config = &app_context.config; + let app_dal_conf = config.get_dal_conf()?; + + APP_DAL_CLIENT.get_or_init(|| AppDalClient::new(app_dal_conf.unwrap_or_default())); + + Ok(()) + } +} diff --git a/crates/recorder/src/dal/config.rs b/crates/recorder/src/dal/config.rs new file mode 100644 index 0000000..e0daaf1 --- /dev/null +++ b/crates/recorder/src/dal/config.rs @@ -0,0 +1,8 @@ +use serde::{Deserialize, Serialize}; + +pub const DAL_CONF_KEY: &str = "dal"; + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)] +pub struct AppDalConfig { + pub data_dir: Option, +} diff --git a/crates/recorder/src/dal/mod.rs b/crates/recorder/src/dal/mod.rs index 7e45f96..a369edf 100644 --- a/crates/recorder/src/dal/mod.rs +++ b/crates/recorder/src/dal/mod.rs @@ -1,74 +1,4 @@ -use bytes::Bytes; -use opendal::{layers::LoggingLayer, services, Operator}; -use quirks_path::{Path, PathBuf}; -use serde::{Deserialize, Serialize}; -use url::Url; -use uuid::Uuid; - -use crate::config::AppDalConf; - -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -#[serde(rename_all = "snake_case")] -pub enum AppDalContentCategory { - Poster, -} - -impl AsRef for AppDalContentCategory { - fn as_ref(&self) -> &str { - match self { - Self::Poster => "poster", - } - } -} - -#[derive(Debug, Clone)] -pub struct AppDalContext { - pub config: AppDalConf, -} - -pub enum DalStoredUrl { - RelativePath { path: String }, - Absolute { url: Url }, -} - -impl AppDalContext { - pub fn new(app_dal_conf: AppDalConf) -> Self { - Self { - config: app_dal_conf, - } - } - - pub async fn store_blob( - &self, - content_category: AppDalContentCategory, - extname: &str, - data: Bytes, - subscriber_pid: &str, - ) -> eyre::Result { - let basename = format!("{}{}", Uuid::new_v4(), extname); - let mut dirname = [subscriber_pid, content_category.as_ref()] - .into_iter() - .map(Path::new) - .collect::(); - - let mut fs_builder = services::Fs::default(); - fs_builder.root(self.config.fs_root.as_str()); - - let fs_op = Operator::new(fs_builder)? - .layer(LoggingLayer::default()) - .finish(); - - fs_op.create_dir(dirname.as_str()).await?; - - let fullname = { - dirname.push(basename); - dirname - }; - - fs_op.write_with(fullname.as_str(), data).await?; - - Ok(DalStoredUrl::RelativePath { - path: fullname.to_string(), - }) - } -} +pub mod client; +pub mod config; +pub use client::{AppDalClient, AppDalInitalizer, DalContentCategory}; +pub use config::{AppDalConfig, DAL_CONF_KEY}; diff --git a/crates/recorder/src/downloaders/bytes.rs b/crates/recorder/src/downloaders/bytes.rs deleted file mode 100644 index 56fbfbf..0000000 --- a/crates/recorder/src/downloaders/bytes.rs +++ /dev/null @@ -1,12 +0,0 @@ -use bytes::Bytes; -use reqwest::IntoUrl; - -use super::defs::DEFAULT_USER_AGENT; - -pub async fn download_bytes(url: T) -> eyre::Result { - let request_client = reqwest::Client::builder() - .user_agent(DEFAULT_USER_AGENT) - .build()?; - let bytes = request_client.get(url).send().await?.bytes().await?; - Ok(bytes) -} diff --git a/crates/recorder/src/downloaders/html.rs b/crates/recorder/src/downloaders/html.rs deleted file mode 100644 index d1eed9e..0000000 --- a/crates/recorder/src/downloaders/html.rs +++ /dev/null @@ -1,11 +0,0 @@ -use reqwest::IntoUrl; - -use super::defs::DEFAULT_USER_AGENT; - -pub async fn download_html(url: U) -> eyre::Result { - let request_client = reqwest::Client::builder() - .user_agent(DEFAULT_USER_AGENT) - .build()?; - let content = request_client.get(url).send().await?.text().await?; - Ok(content) -} diff --git a/crates/recorder/src/downloaders/image.rs b/crates/recorder/src/downloaders/image.rs deleted file mode 100644 index 5316090..0000000 --- a/crates/recorder/src/downloaders/image.rs +++ /dev/null @@ -1,8 +0,0 @@ -use bytes::Bytes; -use reqwest::IntoUrl; - -use super::bytes::download_bytes; - -pub async fn download_image(url: U) -> eyre::Result { - download_bytes(url).await -} diff --git a/crates/recorder/src/downloaders/mod.rs b/crates/recorder/src/downloaders/mod.rs deleted file mode 100644 index 26dd556..0000000 --- a/crates/recorder/src/downloaders/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -pub mod bytes; -pub mod defs; -pub mod error; -pub mod html; -pub mod qbitorrent; -pub mod torrent_downloader; -pub mod image; diff --git a/crates/recorder/src/downloaders/torrent_downloader.rs b/crates/recorder/src/downloaders/torrent_downloader.rs deleted file mode 100644 index 55a1a3b..0000000 --- a/crates/recorder/src/downloaders/torrent_downloader.rs +++ /dev/null @@ -1,96 +0,0 @@ -use downloaders::DownloaderCategory; -use quirks_path::{Path, PathBuf}; -use sea_orm::{ActiveModelTrait, ActiveValue, DatabaseConnection, IntoActiveModel}; -use url::Url; - -use super::{ - defs::{Torrent, TorrentFilter, TorrentSource}, - qbitorrent::QBittorrentDownloader, -}; -use crate::{ - models::{bangumi, downloaders, downloads}, - path::torrent_path::gen_bangumi_sub_path, -}; - -#[async_trait::async_trait] -pub trait TorrentDownloader { - async fn get_torrents_info( - &self, - status_filter: TorrentFilter, - category: Option, - tag: Option, - ) -> eyre::Result>; - - async fn add_torrents( - &self, - source: TorrentSource, - save_path: String, - category: Option<&str>, - ) -> eyre::Result<()>; - - async fn delete_torrents(&self, hashes: Vec) -> eyre::Result<()>; - - async fn rename_torrent_file( - &self, - hash: &str, - old_path: &str, - new_path: &str, - ) -> eyre::Result<()>; - - async fn move_torrents(&self, hashes: Vec, new_path: &str) -> eyre::Result<()>; - - async fn get_torrent_path(&self, hashes: String) -> eyre::Result>; - - async fn check_connection(&self) -> eyre::Result<()>; - - async fn set_torrents_category(&self, hashes: Vec, category: &str) -> eyre::Result<()>; - - async fn add_torrent_tags(&self, hashes: Vec, tags: Vec) -> eyre::Result<()>; - - async fn add_category(&self, category: &str) -> eyre::Result<()>; - - fn get_save_path(&self, sub_path: &Path) -> PathBuf; - - async fn add_downloads_for_bangumi<'a, 'b>( - &self, - db: &'a DatabaseConnection, - downloads: &[&downloads::Model], - mut bangumi: bangumi::Model, - ) -> eyre::Result { - if bangumi.save_path.is_none() { - let gen_sub_path = gen_bangumi_sub_path(&bangumi); - let mut bangumi_active = bangumi.into_active_model(); - bangumi_active.save_path = ActiveValue::Set(Some(gen_sub_path.to_string())); - bangumi = bangumi_active.update(db).await?; - } - - let sub_path = bangumi - .save_path - .as_ref() - .unwrap_or_else(|| unreachable!("must have a sub path")); - - let mut torrent_urls = vec![]; - for m in downloads.iter() { - torrent_urls.push(Url::parse(&m.url as &str)?); - } - - // make sequence to prevent too fast to be banned - for d in downloads.iter() { - let source = TorrentSource::parse(&d.url).await?; - self.add_torrents(source, sub_path.clone(), Some("bangumi")) - .await?; - } - - Ok(bangumi) - } -} - -pub async fn build_torrent_downloader_from_downloader_model( - model: downloaders::Model, -) -> eyre::Result> { - Ok(Box::new(match &model.category { - DownloaderCategory::QBittorrent => { - QBittorrentDownloader::from_downloader_model(model).await? - } - })) -} diff --git a/crates/recorder/src/parsers/defs.rs b/crates/recorder/src/extract/defs.rs similarity index 100% rename from crates/recorder/src/parsers/defs.rs rename to crates/recorder/src/extract/defs.rs diff --git a/crates/recorder/src/parsers/errors.rs b/crates/recorder/src/extract/errors.rs similarity index 68% rename from crates/recorder/src/parsers/errors.rs rename to crates/recorder/src/extract/errors.rs index 3d0d28b..ed03791 100644 --- a/crates/recorder/src/parsers/errors.rs +++ b/crates/recorder/src/extract/errors.rs @@ -12,4 +12,8 @@ pub enum ParseError { expected: String, found: String, }, + #[error("Parse mikan rss {url} format error")] + MikanRssFormatError { url: String }, + #[error("Parse mikan rss item format error, {reason}")] + MikanRssItemFormatError { reason: String }, } diff --git a/crates/recorder/src/extract/html/mod.rs b/crates/recorder/src/extract/html/mod.rs new file mode 100644 index 0000000..e72b2c2 --- /dev/null +++ b/crates/recorder/src/extract/html/mod.rs @@ -0,0 +1,3 @@ +pub mod styles; + +pub use styles::parse_style_attr; diff --git a/crates/recorder/src/extract/html/styles.rs b/crates/recorder/src/extract/html/styles.rs new file mode 100644 index 0000000..7632125 --- /dev/null +++ b/crates/recorder/src/extract/html/styles.rs @@ -0,0 +1,6 @@ +use lightningcss::declaration::DeclarationBlock; + +pub fn parse_style_attr(style_attr: &str) -> Option { + let result = DeclarationBlock::parse_string(style_attr, Default::default()).ok()?; + Some(result) +} diff --git a/crates/recorder/src/extract/mikan/client.rs b/crates/recorder/src/extract/mikan/client.rs new file mode 100644 index 0000000..031bbcd --- /dev/null +++ b/crates/recorder/src/extract/mikan/client.rs @@ -0,0 +1,64 @@ +use std::ops::Deref; + +use loco_rs::app::{AppContext, Initializer}; +use once_cell::sync::OnceCell; + +use super::{AppMikanConfig, MIKAN_BASE_URL}; +use crate::{config::AppConfigExt, fetch::HttpClient}; + +static APP_MIKAN_CLIENT: OnceCell = OnceCell::new(); + +pub struct AppMikanClient { + http_client: HttpClient, + base_url: String, +} + +impl AppMikanClient { + pub fn new(mut config: AppMikanConfig) -> loco_rs::Result { + let http_client = + HttpClient::new(config.http_client.take()).map_err(loco_rs::Error::wrap)?; + let base_url = config + .base_url + .unwrap_or_else(|| String::from(MIKAN_BASE_URL)); + Ok(Self { + http_client, + base_url, + }) + } + + pub fn global() -> &'static AppMikanClient { + APP_MIKAN_CLIENT + .get() + .expect("Global mikan http client is not initialized") + } + + pub fn base_url(&self) -> &str { + &self.base_url + } +} + +impl Deref for AppMikanClient { + type Target = HttpClient; + + fn deref(&self) -> &Self::Target { + &self.http_client + } +} + +pub struct AppMikanClientInitializer; + +#[async_trait::async_trait] +impl Initializer for AppMikanClientInitializer { + fn name(&self) -> String { + "AppMikanClientInitializer".to_string() + } + + async fn before_run(&self, app_context: &AppContext) -> loco_rs::Result<()> { + let config = &app_context.config; + let app_mikan_conf = config.get_mikan_conf()?.unwrap_or_default(); + + APP_MIKAN_CLIENT.get_or_try_init(|| AppMikanClient::new(app_mikan_conf))?; + + Ok(()) + } +} diff --git a/crates/recorder/src/extract/mikan/config.rs b/crates/recorder/src/extract/mikan/config.rs new file mode 100644 index 0000000..770de5e --- /dev/null +++ b/crates/recorder/src/extract/mikan/config.rs @@ -0,0 +1,11 @@ +use serde::{Deserialize, Serialize}; + +use crate::fetch::HttpClientConfig; + +pub const MIKAN_CONF_KEY: &str = "mikan"; + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)] +pub struct AppMikanConfig { + pub http_client: Option, + pub base_url: Option, +} diff --git a/crates/recorder/src/extract/mikan/constants.rs b/crates/recorder/src/extract/mikan/constants.rs new file mode 100644 index 0000000..9e79ef4 --- /dev/null +++ b/crates/recorder/src/extract/mikan/constants.rs @@ -0,0 +1,4 @@ +pub const MIKAN_BUCKET_KEY: &str = "mikan"; +pub const MIKAN_BASE_URL: &str = "https://mikanani.me"; +pub const MIKAN_UNKNOWN_FANSUB_NAME: &str = "生肉/不明字幕"; +pub const MIKAN_UNKNOWN_FANSUB_ID: &str = "202"; diff --git a/crates/recorder/src/extract/mikan/mod.rs b/crates/recorder/src/extract/mikan/mod.rs new file mode 100644 index 0000000..53cc43a --- /dev/null +++ b/crates/recorder/src/extract/mikan/mod.rs @@ -0,0 +1,22 @@ +pub mod client; +pub mod config; +pub mod constants; +pub mod rss_parser; +pub mod web_parser; + +pub use client::{AppMikanClient, AppMikanClientInitializer}; +pub use config::{AppMikanConfig, MIKAN_CONF_KEY}; +pub use constants::{MIKAN_BASE_URL, MIKAN_BUCKET_KEY}; +pub use rss_parser::{ + build_mikan_bangumi_rss_link, build_mikan_subscriber_aggregation_rss_link, + parse_mikan_bangumi_id_from_rss_link, parse_mikan_rss_channel_from_rss_link, + parse_mikan_rss_items_from_rss_link, parse_mikan_subscriber_aggregation_id_from_rss_link, + MikanBangumiAggregationRssChannel, MikanBangumiRssChannel, MikanBangumiRssLink, + MikanRssChannel, MikanRssItem, MikanSubscriberAggregationRssChannel, + MikanSubscriberAggregationRssLink, +}; +pub use web_parser::{ + build_mikan_bangumi_homepage, build_mikan_episode_homepage, + parse_mikan_bangumi_meta_from_mikan_homepage, parse_mikan_episode_meta_from_mikan_homepage, + MikanBangumiMeta, MikanEpisodeMeta, +}; diff --git a/crates/recorder/src/extract/mikan/rss_parser.rs b/crates/recorder/src/extract/mikan/rss_parser.rs new file mode 100644 index 0000000..85dcad4 --- /dev/null +++ b/crates/recorder/src/extract/mikan/rss_parser.rs @@ -0,0 +1,353 @@ +use std::ops::Deref; + +use chrono::DateTime; +use itertools::Itertools; +use reqwest::IntoUrl; +use serde::{Deserialize, Serialize}; +use torrent::core::BITTORRENT_MIME_TYPE; +use url::Url; + +use super::{ + web_parser::{parse_mikan_episode_id_from_homepage, MikanEpisodeHomepage}, + AppMikanClient, +}; +use crate::{extract::errors::ParseError, fetch::bytes::download_bytes_with_client}; + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub struct MikanRssItem { + pub title: String, + pub homepage: Url, + pub url: Url, + pub content_length: Option, + pub mime: String, + pub pub_date: Option, + pub mikan_episode_id: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub struct MikanBangumiRssChannel { + pub name: String, + pub url: Url, + pub mikan_bangumi_id: String, + pub mikan_fansub_id: String, + pub items: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub struct MikanBangumiAggregationRssChannel { + pub name: String, + pub url: Url, + pub mikan_bangumi_id: String, + pub items: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub struct MikanSubscriberAggregationRssChannel { + pub mikan_aggregation_id: String, + pub url: Url, + pub items: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub enum MikanRssChannel { + Bangumi(MikanBangumiRssChannel), + BangumiAggregation(MikanBangumiAggregationRssChannel), + SubscriberAggregation(MikanSubscriberAggregationRssChannel), +} + +impl MikanRssChannel { + pub fn items(&self) -> &[MikanRssItem] { + match &self { + Self::Bangumi(MikanBangumiRssChannel { items, .. }) + | Self::BangumiAggregation(MikanBangumiAggregationRssChannel { items, .. }) + | Self::SubscriberAggregation(MikanSubscriberAggregationRssChannel { items, .. }) => { + items + } + } + } + + pub fn into_items(self) -> Vec { + match self { + Self::Bangumi(MikanBangumiRssChannel { items, .. }) + | Self::BangumiAggregation(MikanBangumiAggregationRssChannel { items, .. }) + | Self::SubscriberAggregation(MikanSubscriberAggregationRssChannel { items, .. }) => { + items + } + } + } + + pub fn name(&self) -> Option<&str> { + match &self { + Self::Bangumi(MikanBangumiRssChannel { name, .. }) + | Self::BangumiAggregation(MikanBangumiAggregationRssChannel { name, .. }) => { + Some(name.as_str()) + } + Self::SubscriberAggregation(MikanSubscriberAggregationRssChannel { .. }) => None, + } + } + + pub fn url(&self) -> &Url { + match &self { + Self::Bangumi(MikanBangumiRssChannel { url, .. }) + | Self::BangumiAggregation(MikanBangumiAggregationRssChannel { url, .. }) + | Self::SubscriberAggregation(MikanSubscriberAggregationRssChannel { url, .. }) => url, + } + } +} + +impl TryFrom for MikanRssItem { + type Error = ParseError; + + fn try_from(item: rss::Item) -> Result { + let mime_type = item + .enclosure() + .map(|x| x.mime_type.to_string()) + .unwrap_or_default(); + if mime_type == BITTORRENT_MIME_TYPE { + let enclosure = item.enclosure.unwrap(); + + let homepage = item + .link + .ok_or_else(|| ParseError::MikanRssItemFormatError { + reason: String::from("must to have link for homepage"), + })?; + + let homepage = Url::parse(&homepage)?; + + let enclosure_url = Url::parse(&enclosure.url)?; + + let MikanEpisodeHomepage { + mikan_episode_id, .. + } = parse_mikan_episode_id_from_homepage(&homepage).ok_or_else(|| { + ParseError::MikanRssItemFormatError { + reason: String::from("homepage link format invalid"), + } + })?; + + Ok(MikanRssItem { + title: item.title.unwrap_or_default(), + homepage, + url: enclosure_url, + content_length: enclosure.length.parse().ok(), + mime: enclosure.mime_type, + pub_date: item + .pub_date + .and_then(|s| DateTime::parse_from_rfc2822(&s).ok()) + .map(|s| s.timestamp_millis()), + mikan_episode_id, + }) + } else { + Err(ParseError::MimeError { + expected: String::from(BITTORRENT_MIME_TYPE), + found: mime_type, + desc: String::from("MikanRssItem"), + }) + } + } +} + +#[derive(Debug, Clone)] +pub struct MikanBangumiRssLink { + pub mikan_bangumi_id: String, + pub mikan_fansub_id: Option, +} + +#[derive(Debug, Clone)] +pub struct MikanSubscriberAggregationRssLink { + pub mikan_aggregation_id: String, +} + +pub fn build_mikan_bangumi_rss_link( + mikan_base_url: &str, + mikan_bangumi_id: &str, + mikan_fansub_id: Option<&str>, +) -> eyre::Result { + let mut url = Url::parse(mikan_base_url)?; + url.set_path("/RSS/Bangumi"); + url.query_pairs_mut() + .append_pair("bangumiId", mikan_bangumi_id); + if let Some(mikan_fansub_id) = mikan_fansub_id { + url.query_pairs_mut() + .append_pair("subgroupid", mikan_fansub_id); + }; + Ok(url) +} + +pub fn build_mikan_subscriber_aggregation_rss_link( + mikan_base_url: &str, + mikan_aggregation_id: &str, +) -> eyre::Result { + let mut url = Url::parse(mikan_base_url)?; + url.set_path("/RSS/MyBangumi"); + url.query_pairs_mut() + .append_pair("token", mikan_aggregation_id); + Ok(url) +} + +pub fn parse_mikan_bangumi_id_from_rss_link(url: &Url) -> Option { + if url.path() == "/RSS/Bangumi" { + url.query_pairs() + .find(|(k, _)| k == "bangumiId") + .map(|(_, v)| MikanBangumiRssLink { + mikan_bangumi_id: v.to_string(), + mikan_fansub_id: url + .query_pairs() + .find(|(k, _)| k == "subgroupid") + .map(|(_, v)| v.to_string()), + }) + } else { + None + } +} + +pub fn parse_mikan_subscriber_aggregation_id_from_rss_link( + url: &Url, +) -> Option { + if url.path() == "/RSS/MyBangumi" { + url.query_pairs().find(|(k, _)| k == "token").map(|(_, v)| { + MikanSubscriberAggregationRssLink { + mikan_aggregation_id: v.to_string(), + } + }) + } else { + None + } +} + +pub async fn parse_mikan_rss_items_from_rss_link( + client: Option<&AppMikanClient>, + url: impl IntoUrl, +) -> eyre::Result> { + let channel = parse_mikan_rss_channel_from_rss_link(client, url).await?; + + Ok(channel.into_items()) +} + +pub async fn parse_mikan_rss_channel_from_rss_link( + client: Option<&AppMikanClient>, + url: impl IntoUrl, +) -> eyre::Result { + let http_client = client.map(|s| s.deref()); + let bytes = download_bytes_with_client(http_client, url.as_str()).await?; + + let channel = rss::Channel::read_from(&bytes[..])?; + + let channel_link = Url::parse(channel.link())?; + + if let Some(MikanBangumiRssLink { + mikan_bangumi_id, + mikan_fansub_id, + }) = parse_mikan_bangumi_id_from_rss_link(&channel_link) + { + let channel_name = channel.title().replace("Mikan Project - ", ""); + + let items = channel + .items + .into_iter() + // @TODO log error + .flat_map(MikanRssItem::try_from) + .collect_vec(); + + if let Some(mikan_fansub_id) = mikan_fansub_id { + Ok(MikanRssChannel::Bangumi(MikanBangumiRssChannel { + name: channel_name, + mikan_bangumi_id, + mikan_fansub_id, + url: channel_link, + items, + })) + } else { + Ok(MikanRssChannel::BangumiAggregation( + MikanBangumiAggregationRssChannel { + name: channel_name, + mikan_bangumi_id, + url: channel_link, + items, + }, + )) + } + } else if let Some(MikanSubscriberAggregationRssLink { + mikan_aggregation_id, + .. + }) = parse_mikan_subscriber_aggregation_id_from_rss_link(&channel_link) + { + let items = channel + .items + .into_iter() + // @TODO log error + .flat_map(MikanRssItem::try_from) + .collect_vec(); + + return Ok(MikanRssChannel::SubscriberAggregation( + MikanSubscriberAggregationRssChannel { + mikan_aggregation_id, + items, + url: channel_link, + }, + )); + } else { + return Err(ParseError::MikanRssFormatError { + url: url.as_str().into(), + } + .into()); + } +} + +#[cfg(test)] +mod tests { + use std::assert_matches::assert_matches; + + use torrent::core::BITTORRENT_MIME_TYPE; + + use crate::extract::mikan::{ + parse_mikan_rss_channel_from_rss_link, MikanBangumiAggregationRssChannel, + MikanBangumiRssChannel, MikanRssChannel, + }; + + #[tokio::test] + pub async fn test_parse_mikan_rss_channel_from_rss_link() { + { + let bangumi_url = "https://mikanani.me/RSS/Bangumi?bangumiId=3141&subgroupid=370"; + + let channel = parse_mikan_rss_channel_from_rss_link(None, bangumi_url) + .await + .expect("should get mikan channel from rss url"); + + assert_matches!( + &channel, + MikanRssChannel::Bangumi(MikanBangumiRssChannel { .. }) + ); + + assert_matches!(&channel.name(), Some("葬送的芙莉莲")); + + let items = channel.items(); + let first_sub_item = items + .first() + .expect("mikan subscriptions should have at least one subs"); + + assert_eq!(first_sub_item.mime, BITTORRENT_MIME_TYPE); + + assert!(&first_sub_item + .homepage + .as_str() + .starts_with("https://mikanani.me/Home/Episode")); + + let name = first_sub_item.title.as_str(); + assert!(name.contains("葬送的芙莉莲")); + } + { + let bangumi_url = "https://mikanani.me/RSS/Bangumi?bangumiId=3416"; + + let channel = parse_mikan_rss_channel_from_rss_link(None, bangumi_url) + .await + .expect("should get mikan channel from rss url"); + + assert_matches!( + &channel, + MikanRssChannel::BangumiAggregation(MikanBangumiAggregationRssChannel { .. }) + ); + + assert_matches!(&channel.name(), Some("叹气的亡灵想隐退")); + } + } +} diff --git a/crates/recorder/src/extract/mikan/web_parser.rs b/crates/recorder/src/extract/mikan/web_parser.rs new file mode 100644 index 0000000..e0c8102 --- /dev/null +++ b/crates/recorder/src/extract/mikan/web_parser.rs @@ -0,0 +1,493 @@ +use std::ops::Deref; + +use bytes::Bytes; +use eyre::ContextCompat; +use html_escape::decode_html_entities; +use itertools::Itertools; +use lazy_static::lazy_static; +use lightningcss::{properties::Property, values::image::Image as CSSImage}; +use loco_rs::app::AppContext; +use regex::Regex; +use scraper::Html; +use url::Url; + +use super::{ + parse_mikan_bangumi_id_from_rss_link, AppMikanClient, MikanBangumiRssLink, MIKAN_BUCKET_KEY, +}; +use crate::{ + app::AppContextExt, + dal::DalContentCategory, + extract::html::parse_style_attr, + fetch::{html::download_html_with_client, image::download_image_with_client}, + models::subscribers, +}; + +#[derive(Clone, Debug, PartialEq)] +pub struct MikanEpisodeMeta { + pub homepage: Url, + pub origin_poster_src: Option, + pub bangumi_title: String, + pub episode_title: String, + pub fansub: String, + pub mikan_bangumi_id: String, + pub mikan_fansub_id: String, + pub mikan_episode_id: String, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct MikanBangumiMeta { + pub homepage: Url, + pub origin_poster_src: Option, + pub bangumi_title: String, + pub mikan_bangumi_id: String, + pub mikan_fansub_id: Option, + pub fansub: Option, + pub mikan_fansub_candidates: Vec<(String, String)>, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct MikanBangumiPosterMeta { + pub origin_poster_src: Url, + pub poster_data: Option, + pub poster_src: Option, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct MikanEpisodeHomepage { + pub mikan_episode_id: String, +} + +lazy_static! { + static ref MIKAN_TITLE_SEASON: Regex = Regex::new("第.*季").unwrap(); +} + +pub fn build_mikan_bangumi_homepage( + mikan_base_url: &str, + mikan_bangumi_id: &str, + mikan_fansub_id: Option<&str>, +) -> eyre::Result { + let mut url = Url::parse(mikan_base_url)?; + url.set_path(&format!("/Home/Bangumi/{mikan_bangumi_id}")); + url.set_fragment(mikan_fansub_id); + Ok(url) +} + +pub fn build_mikan_episode_homepage( + mikan_base_url: &str, + mikan_episode_id: &str, +) -> eyre::Result { + let mut url = Url::parse(mikan_base_url)?; + url.set_path(&format!("/Home/Episode/{mikan_episode_id}")); + Ok(url) +} + +pub fn parse_mikan_episode_id_from_homepage(url: &Url) -> Option { + if url.path().starts_with("/Home/Episode/") { + let mikan_episode_id = url.path().replace("/Home/Episode/", ""); + Some(MikanEpisodeHomepage { mikan_episode_id }) + } else { + None + } +} + +pub async fn parse_mikan_bangumi_poster_from_origin_poster_src( + client: Option<&AppMikanClient>, + origin_poster_src: Url, +) -> eyre::Result { + let http_client = client.map(|s| s.deref()); + let poster_data = download_image_with_client(http_client, origin_poster_src.clone()).await?; + Ok(MikanBangumiPosterMeta { + origin_poster_src, + poster_data: Some(poster_data), + poster_src: None, + }) +} + +pub async fn parse_mikan_bangumi_poster_from_origin_poster_src_with_cache( + ctx: &AppContext, + origin_poster_src: Url, + subscriber_id: i32, +) -> eyre::Result { + let dal_client = ctx.get_dal_client(); + let mikan_client = ctx.get_mikan_client(); + let subscriber_pid = &subscribers::Model::find_pid_by_id_with_cache(ctx, subscriber_id).await?; + if let Some(poster_src) = dal_client + .exists_object( + DalContentCategory::Image, + subscriber_pid, + Some(MIKAN_BUCKET_KEY), + &origin_poster_src.path().replace("/images/Bangumi/", ""), + ) + .await? + { + return Ok(MikanBangumiPosterMeta { + origin_poster_src, + poster_data: None, + poster_src: Some(poster_src.to_string()), + }); + } + + let poster_data = + download_image_with_client(Some(mikan_client.deref()), origin_poster_src.clone()).await?; + + let poster_str = dal_client + .store_object( + DalContentCategory::Image, + subscriber_pid, + Some(MIKAN_BUCKET_KEY), + &origin_poster_src.path().replace("/images/Bangumi/", ""), + poster_data.clone(), + ) + .await?; + + Ok(MikanBangumiPosterMeta { + origin_poster_src, + poster_data: Some(poster_data), + poster_src: Some(poster_str.to_string()), + }) +} + +pub async fn parse_mikan_bangumi_meta_from_mikan_homepage( + client: Option<&AppMikanClient>, + url: Url, +) -> eyre::Result { + let http_client = client.map(|s| s.deref()); + let url_host = url.origin().unicode_serialization(); + let content = download_html_with_client(http_client, url.as_str()).await?; + let html = Html::parse_document(&content); + + let bangumi_fansubs = html + .select(&scraper::Selector::parse(".subgroup-text").unwrap()) + .filter_map(|el| { + if let (Some(fansub_id), Some(fansub_name)) = ( + el.value() + .attr("id") + .map(|s| decode_html_entities(s).trim().to_string()), + el.select(&scraper::Selector::parse("a:nth-child(1)").unwrap()) + .next() + .map(|child| { + let mut s = String::from( + child + .prev_sibling() + .and_then(|t| t.value().as_text()) + .map(|s| s.trim()) + .unwrap_or_default(), + ); + s.extend(child.text()); + decode_html_entities(&s).trim().to_string() + }), + ) { + Some((fansub_id, fansub_name)) + } else { + None + } + }) + .collect_vec(); + + let fansub_info = url.fragment().and_then(|b| { + bangumi_fansubs + .iter() + .find_map(|(id, name)| if id == b { Some((id, name)) } else { None }) + }); + + let bangumi_title = html + .select(&scraper::Selector::parse(".bangumi-title").unwrap()) + .next() + .map(|el| { + decode_html_entities(&el.text().collect::()) + .trim() + .to_string() + }) + .and_then(|title| if title.is_empty() { None } else { Some(title) }) + .wrap_err_with(|| { + // todo: error handler + format!("Missing mikan bangumi official title for {}", url) + })?; + + let MikanBangumiRssLink { + mikan_bangumi_id, .. + } = html + .select(&scraper::Selector::parse(".bangumi-title > .mikan-rss").unwrap()) + .next() + .and_then(|el| el.value().attr("href")) + .as_ref() + .and_then(|s| url.join(s).ok()) + .and_then(|rss_link_url| parse_mikan_bangumi_id_from_rss_link(&rss_link_url)) + .wrap_err_with(|| { + // todo: error handler + format!("Missing mikan bangumi rss link or error format for {}", url) + })?; + + let origin_poster_src = html + .select(&scraper::Selector::parse(".bangumi-poster").unwrap()) + .next() + .and_then(|el| el.value().attr("style")) + .as_ref() + .and_then(|s| parse_style_attr(s)) + .and_then(|style| { + style.iter().find_map(|(prop, _)| { + match prop { + Property::BackgroundImage(images) => { + for img in images { + if let CSSImage::Url(path) = img { + if let Ok(url) = + Url::parse(&url_host).and_then(|s| s.join(path.url.trim())) + { + return Some(url); + } + } + } + } + Property::Background(backgrounds) => { + for bg in backgrounds { + if let CSSImage::Url(path) = &bg.image { + if let Ok(url) = + Url::parse(&url_host).and_then(|s| s.join(path.url.trim())) + { + return Some(url); + } + } + } + } + _ => {} + } + None + }) + }) + .map(|mut origin_poster_src| { + origin_poster_src.set_query(None); + origin_poster_src + }); + + Ok(MikanBangumiMeta { + homepage: url, + bangumi_title, + origin_poster_src, + mikan_bangumi_id, + fansub: fansub_info.map(|s| s.1.to_string()), + mikan_fansub_id: fansub_info.map(|s| s.0.to_string()), + mikan_fansub_candidates: bangumi_fansubs.clone(), + }) +} + +pub async fn parse_mikan_episode_meta_from_mikan_homepage( + client: Option<&AppMikanClient>, + url: Url, +) -> eyre::Result { + let http_client = client.map(|s| s.deref()); + let url_host = url.origin().unicode_serialization(); + let content = download_html_with_client(http_client, url.as_str()).await?; + + let html = Html::parse_document(&content); + + let bangumi_title = html + .select(&scraper::Selector::parse(".bangumi-title").unwrap()) + .next() + .map(|el| { + decode_html_entities(&el.text().collect::()) + .trim() + .to_string() + }) + .and_then(|title| if title.is_empty() { None } else { Some(title) }) + .wrap_err_with(|| { + // todo: error handler + format!("Missing mikan bangumi official title for {}", url) + })?; + + let episode_title = html + .select(&scraper::Selector::parse("title").unwrap()) + .next() + .map(|el| { + decode_html_entities(&el.text().collect::()) + .replace(" - Mikan Project", "") + .trim() + .to_string() + }) + .and_then(|title| if title.is_empty() { None } else { Some(title) }) + .wrap_err_with(|| { + // todo: error handler + format!("Missing mikan episode official title for {}", url) + })?; + + let (mikan_bangumi_id, mikan_fansub_id) = html + .select(&scraper::Selector::parse(".bangumi-title > .mikan-rss").unwrap()) + .next() + .and_then(|el| el.value().attr("href")) + .as_ref() + .and_then(|s| url.join(s).ok()) + .and_then(|rss_link_url| parse_mikan_bangumi_id_from_rss_link(&rss_link_url)) + .and_then( + |MikanBangumiRssLink { + mikan_bangumi_id, + mikan_fansub_id, + .. + }| { + mikan_fansub_id.map(|mikan_fansub_id| (mikan_bangumi_id, mikan_fansub_id)) + }, + ) + .wrap_err_with(|| { + // todo: error handler + format!("Missing mikan bangumi rss link or error format for {}", url) + })?; + + let fansub = html + .select(&scraper::Selector::parse(".bangumi-info>.magnet-link-wrap").unwrap()) + .next() + .map(|el| { + decode_html_entities(&el.text().collect::()) + .trim() + .to_string() + }) + .wrap_err_with(|| { + // todo: error handler + format!("Missing mikan bangumi fansub name for {}", url) + })?; + + let origin_poster_src = html + .select(&scraper::Selector::parse(".bangumi-poster").unwrap()) + .next() + .and_then(|el| el.value().attr("style")) + .as_ref() + .and_then(|s| parse_style_attr(s)) + .and_then(|style| { + style.iter().find_map(|(prop, _)| { + match prop { + Property::BackgroundImage(images) => { + for img in images { + if let CSSImage::Url(path) = img { + if let Ok(url) = + Url::parse(&url_host).and_then(|s| s.join(path.url.trim())) + { + return Some(url); + } + } + } + } + Property::Background(backgrounds) => { + for bg in backgrounds { + if let CSSImage::Url(path) = &bg.image { + if let Ok(url) = + Url::parse(&url_host).and_then(|s| s.join(path.url.trim())) + { + return Some(url); + } + } + } + } + _ => {} + } + None + }) + }) + .map(|mut origin_poster_src| { + origin_poster_src.set_query(None); + origin_poster_src + }); + + let MikanEpisodeHomepage { + mikan_episode_id, .. + } = parse_mikan_episode_id_from_homepage(&url) + .wrap_err_with(|| format!("Failed to extract mikan_episode_id from {}", &url))?; + + Ok(MikanEpisodeMeta { + mikan_bangumi_id, + mikan_fansub_id, + bangumi_title, + episode_title, + homepage: url, + origin_poster_src, + fansub, + mikan_episode_id, + }) +} + +#[cfg(test)] +mod test { + use std::assert_matches::assert_matches; + + use url::Url; + use zune_image::{codecs::ImageFormat, image::Image}; + + use super::{ + parse_mikan_bangumi_meta_from_mikan_homepage, + parse_mikan_bangumi_poster_from_origin_poster_src, + parse_mikan_episode_meta_from_mikan_homepage, + }; + + #[tokio::test] + async fn test_parse_mikan_episode() { + let test_fn = async || -> eyre::Result<()> { + let url_str = + "https://mikanani.me/Home/Episode/475184dce83ea2b82902592a5ac3343f6d54b36a"; + let url = Url::parse(url_str)?; + + let ep_meta = parse_mikan_episode_meta_from_mikan_homepage(None, url.clone()).await?; + + assert_eq!(ep_meta.homepage, url); + assert_eq!(ep_meta.bangumi_title, "葬送的芙莉莲"); + assert_eq!( + ep_meta.origin_poster_src, + Some(Url::parse( + "https://mikanani.me/images/Bangumi/202309/5ce9fed1.jpg" + )?) + ); + assert_eq!(ep_meta.fansub, "LoliHouse"); + assert_eq!(ep_meta.mikan_fansub_id, "370"); + assert_eq!(ep_meta.mikan_bangumi_id, "3141"); + + assert_matches!(ep_meta.origin_poster_src, Some(..)); + + let bgm_poster = parse_mikan_bangumi_poster_from_origin_poster_src( + None, + ep_meta.origin_poster_src.unwrap(), + ) + .await?; + let u8_data = bgm_poster.poster_data.expect("should have poster data"); + let image = Image::read(u8_data.to_vec(), Default::default()); + assert!( + image.is_ok_and(|img| img + .metadata() + .get_image_format() + .is_some_and(|fmt| matches!(fmt, ImageFormat::JPEG))), + "should start with valid jpeg data magic number" + ); + + Ok(()) + }; + + test_fn().await.expect("test parse mikan failed"); + } + + #[tokio::test] + async fn test_parse_mikan_bangumi() { + let test_fn = async || -> eyre::Result<()> { + let url_str = "https://mikanani.me/Home/Bangumi/3416#370"; + let url = Url::parse(url_str)?; + + let bgm_meta = parse_mikan_bangumi_meta_from_mikan_homepage(None, url.clone()).await?; + + assert_eq!(bgm_meta.homepage, url); + assert_eq!(bgm_meta.bangumi_title, "叹气的亡灵想隐退"); + assert_eq!( + bgm_meta.origin_poster_src, + Some(Url::parse( + "https://mikanani.me/images/Bangumi/202410/480ef127.jpg" + )?) + ); + assert_eq!(bgm_meta.fansub, Some(String::from("LoliHouse"))); + assert_eq!(bgm_meta.mikan_fansub_id, Some(String::from("370"))); + assert_eq!(bgm_meta.mikan_bangumi_id, "3416"); + + assert_eq!( + bgm_meta.homepage.as_str(), + "https://mikanani.me/Home/Bangumi/3416#370" + ); + + assert_eq!(bgm_meta.mikan_fansub_candidates.len(), 6); + + Ok(()) + }; + + test_fn().await.expect("test parse mikan failed"); + } +} diff --git a/crates/recorder/src/parsers/mod.rs b/crates/recorder/src/extract/mod.rs similarity index 68% rename from crates/recorder/src/parsers/mod.rs rename to crates/recorder/src/extract/mod.rs index 1ffecdc..410f0e4 100644 --- a/crates/recorder/src/parsers/mod.rs +++ b/crates/recorder/src/extract/mod.rs @@ -2,6 +2,5 @@ pub mod defs; pub mod errors; pub mod html; pub mod mikan; -pub mod raw; -pub mod title_parser; +pub mod rawname; pub mod torrent; diff --git a/crates/recorder/src/extract/rawname/mod.rs b/crates/recorder/src/extract/rawname/mod.rs new file mode 100644 index 0000000..822392a --- /dev/null +++ b/crates/recorder/src/extract/rawname/mod.rs @@ -0,0 +1,5 @@ +pub mod parser; + +pub use parser::{ + extract_season_from_title_body, parse_episode_meta_from_raw_name, RawEpisodeMeta, +}; diff --git a/crates/recorder/src/parsers/raw/raw_ep_parser.rs b/crates/recorder/src/extract/rawname/parser.rs similarity index 97% rename from crates/recorder/src/parsers/raw/raw_ep_parser.rs rename to crates/recorder/src/extract/rawname/parser.rs index 8dcc02e..63eefbd 100644 --- a/crates/recorder/src/parsers/raw/raw_ep_parser.rs +++ b/crates/recorder/src/extract/rawname/parser.rs @@ -5,7 +5,7 @@ use lazy_static::lazy_static; use regex::Regex; use serde::{Deserialize, Serialize}; -use crate::parsers::defs::{DIGIT_1PLUS_REG, ZH_NUM_MAP, ZH_NUM_RE}; +use crate::extract::defs::{DIGIT_1PLUS_REG, ZH_NUM_MAP, ZH_NUM_RE}; const NAME_EXTRACT_REPLACE_ADHOC1_REPLACED: &str = "$1/$2"; @@ -43,19 +43,19 @@ lazy_static! { #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct RawEpisodeMeta { - name_en: Option, - name_en_no_season: Option, - name_jp: Option, - name_jp_no_season: Option, - name_zh: Option, - name_zh_no_season: Option, - season: i32, - season_raw: Option, - episode_index: i32, - sub: Option, - source: Option, - fansub: Option, - resolution: Option, + pub name_en: Option, + pub name_en_no_season: Option, + pub name_jp: Option, + pub name_jp_no_season: Option, + pub name_zh: Option, + pub name_zh_no_season: Option, + pub season: i32, + pub season_raw: Option, + pub episode_index: i32, + pub subtitle: Option, + pub source: Option, + pub fansub: Option, + pub resolution: Option, } fn extract_fansub(raw_name: &str) -> Option<&str> { @@ -110,7 +110,7 @@ fn title_body_pre_process(title_body: &str, fansub: Option<&str>) -> eyre::Resul Ok(raw.to_string()) } -fn extract_season_from_title_body(title_body: &str) -> (String, Option, i32) { +pub fn extract_season_from_title_body(title_body: &str) -> (String, Option, i32) { let name_and_season = EN_BRACKET_SPLIT_RE.replace_all(title_body, " "); let seasons = SEASON_EXTRACT_SEASON_ALL_RE .find(&name_and_season) @@ -300,7 +300,7 @@ pub fn parse_episode_meta_from_raw_name(s: &str) -> eyre::Result season, season_raw, episode_index, - sub, + subtitle: sub, source, fansub: fansub.map(|s| s.to_string()), resolution, diff --git a/crates/recorder/src/extract/torrent/mod.rs b/crates/recorder/src/extract/torrent/mod.rs new file mode 100644 index 0000000..5feedaf --- /dev/null +++ b/crates/recorder/src/extract/torrent/mod.rs @@ -0,0 +1,3 @@ +mod parser; + +pub use parser::*; diff --git a/crates/recorder/src/parsers/torrent/torrent_ep_parser.rs b/crates/recorder/src/extract/torrent/parser.rs similarity index 97% rename from crates/recorder/src/parsers/torrent/torrent_ep_parser.rs rename to crates/recorder/src/extract/torrent/parser.rs index caa3257..371c7fa 100644 --- a/crates/recorder/src/parsers/torrent/torrent_ep_parser.rs +++ b/crates/recorder/src/extract/torrent/parser.rs @@ -5,7 +5,7 @@ use quirks_path::Path; use regex::Regex; use serde::{Deserialize, Serialize}; -use crate::parsers::defs::SUBTITLE_LANG; +use crate::extract::defs::SUBTITLE_LANG; lazy_static! { static ref TORRENT_EP_PARSE_RULES: Vec = { @@ -52,11 +52,11 @@ fn get_fansub(group_and_title: &str) -> (Option<&str>, &str) { .filter(|s| !s.is_empty()) .collect::>(); - match (n.get(0), n.get(1)) { + match (n.first(), n.get(1)) { (None, None) => (None, ""), (Some(n0), None) => (None, *n0), (Some(n0), Some(n1)) => { - if GET_FANSUB_FULL_MATCH_RE.is_match(*n1) { + if GET_FANSUB_FULL_MATCH_RE.is_match(n1) { (None, group_and_title) } else { (Some(*n0), *n1) @@ -94,7 +94,7 @@ fn get_subtitle_lang(media_name: &str) -> Option<&str> { return Some(lang); } } - return None; + None } pub fn parse_episode_media_meta_from_torrent( @@ -272,7 +272,7 @@ mod tests { let expected: Option = serde_json::from_str(expected).ok(); let found_raw = parse_episode_subtitle_meta_from_torrent(Path::new(raw_name), None, None); - let found = found_raw.as_ref().ok().map(|s| s.clone()); + let found = found_raw.as_ref().ok().cloned(); if expected != found { if found_raw.is_ok() { @@ -293,7 +293,7 @@ mod tests { } else { let expected: Option = serde_json::from_str(expected).ok(); let found_raw = parse_episode_media_meta_from_torrent(Path::new(raw_name), None, None); - let found = found_raw.as_ref().ok().map(|s| s.clone()); + let found = found_raw.as_ref().ok().cloned(); if expected != found { if found_raw.is_ok() { diff --git a/crates/recorder/src/fetch/bytes.rs b/crates/recorder/src/fetch/bytes.rs new file mode 100644 index 0000000..cee96bb --- /dev/null +++ b/crates/recorder/src/fetch/bytes.rs @@ -0,0 +1,24 @@ +use bytes::Bytes; +use reqwest::IntoUrl; + +use super::{core::DEFAULT_HTTP_CLIENT_USER_AGENT, HttpClient}; + +pub async fn download_bytes(url: T) -> eyre::Result { + let request_client = reqwest::Client::builder() + .user_agent(DEFAULT_HTTP_CLIENT_USER_AGENT) + .build()?; + let bytes = request_client.get(url).send().await?.bytes().await?; + Ok(bytes) +} + +pub async fn download_bytes_with_client( + client: Option<&HttpClient>, + url: T, +) -> eyre::Result { + if let Some(client) = client { + let bytes = client.get(url).send().await?.bytes().await?; + Ok(bytes) + } else { + download_bytes(url).await + } +} diff --git a/crates/recorder/src/fetch/client.rs b/crates/recorder/src/fetch/client.rs new file mode 100644 index 0000000..6007975 --- /dev/null +++ b/crates/recorder/src/fetch/client.rs @@ -0,0 +1,96 @@ +use std::{ops::Deref, time::Duration}; + +use axum::http::Extensions; +use leaky_bucket::RateLimiter; +use reqwest::{ClientBuilder, Request, Response}; +use reqwest_middleware::{ + ClientBuilder as ClientWithMiddlewareBuilder, ClientWithMiddleware, Next, +}; +use reqwest_retry::{policies::ExponentialBackoff, RetryTransientMiddleware}; +use reqwest_tracing::TracingMiddleware; +use serde::{Deserialize, Serialize}; +use serde_with::serde_as; + +use super::DEFAULT_HTTP_CLIENT_USER_AGENT; + +#[serde_as] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)] +pub struct HttpClientConfig { + pub exponential_backoff_max_retries: Option, + pub leaky_bucket_max_tokens: Option, + pub leaky_bucket_initial_tokens: Option, + pub leaky_bucket_refill_tokens: Option, + #[serde_as(as = "Option")] + pub leaky_bucket_refill_interval: Option, + pub user_agent: Option, +} + +pub struct HttpClient { + client: ClientWithMiddleware, +} + +impl Deref for HttpClient { + type Target = ClientWithMiddleware; + + fn deref(&self) -> &Self::Target { + &self.client + } +} + +pub struct RateLimiterMiddleware { + rate_limiter: RateLimiter, +} + +#[async_trait::async_trait] +impl reqwest_middleware::Middleware for RateLimiterMiddleware { + async fn handle( + &self, + req: Request, + extensions: &'_ mut Extensions, + next: Next<'_>, + ) -> reqwest_middleware::Result { + self.rate_limiter.acquire_one().await; + next.run(req, extensions).await + } +} + +impl HttpClient { + pub fn new(config: Option) -> reqwest::Result { + let mut config = config.unwrap_or_default(); + let retry_policy = ExponentialBackoff::builder() + .build_with_max_retries(config.exponential_backoff_max_retries.take().unwrap_or(3)); + let rate_limiter = RateLimiter::builder() + .max(config.leaky_bucket_max_tokens.take().unwrap_or(3) as usize) + .initial( + config + .leaky_bucket_initial_tokens + .take() + .unwrap_or_default() as usize, + ) + .refill(config.leaky_bucket_refill_tokens.take().unwrap_or(1) as usize) + .interval( + config + .leaky_bucket_refill_interval + .take() + .unwrap_or_else(|| Duration::from_millis(500)), + ) + .build(); + + let client = ClientBuilder::new() + .user_agent( + config + .user_agent + .take() + .unwrap_or_else(|| DEFAULT_HTTP_CLIENT_USER_AGENT.to_owned()), + ) + .build()?; + + Ok(Self { + client: ClientWithMiddlewareBuilder::new(client) + .with(TracingMiddleware::default()) + .with(RateLimiterMiddleware { rate_limiter }) + .with(RetryTransientMiddleware::new_with_policy(retry_policy)) + .build(), + }) + } +} diff --git a/crates/recorder/src/fetch/core.rs b/crates/recorder/src/fetch/core.rs new file mode 100644 index 0000000..bb7af91 --- /dev/null +++ b/crates/recorder/src/fetch/core.rs @@ -0,0 +1 @@ +pub const DEFAULT_HTTP_CLIENT_USER_AGENT: &str = "Wget/1.13.4 (linux-gnu)"; diff --git a/crates/recorder/src/fetch/html.rs b/crates/recorder/src/fetch/html.rs new file mode 100644 index 0000000..ab6ce9f --- /dev/null +++ b/crates/recorder/src/fetch/html.rs @@ -0,0 +1,23 @@ +use reqwest::IntoUrl; + +use super::{core::DEFAULT_HTTP_CLIENT_USER_AGENT, HttpClient}; + +pub async fn download_html(url: U) -> eyre::Result { + let request_client = reqwest::Client::builder() + .user_agent(DEFAULT_HTTP_CLIENT_USER_AGENT) + .build()?; + let content = request_client.get(url).send().await?.text().await?; + Ok(content) +} + +pub async fn download_html_with_client( + client: Option<&HttpClient>, + url: T, +) -> eyre::Result { + if let Some(client) = client { + let content = client.get(url).send().await?.text().await?; + Ok(content) + } else { + download_html(url).await + } +} diff --git a/crates/recorder/src/fetch/image.rs b/crates/recorder/src/fetch/image.rs new file mode 100644 index 0000000..454f57d --- /dev/null +++ b/crates/recorder/src/fetch/image.rs @@ -0,0 +1,18 @@ +use bytes::Bytes; +use reqwest::IntoUrl; + +use super::{ + bytes::{download_bytes, download_bytes_with_client}, + HttpClient, +}; + +pub async fn download_image(url: U) -> eyre::Result { + download_bytes(url).await +} + +pub async fn download_image_with_client( + client: Option<&HttpClient>, + url: T, +) -> eyre::Result { + download_bytes_with_client(client, url).await +} diff --git a/crates/recorder/src/fetch/mod.rs b/crates/recorder/src/fetch/mod.rs new file mode 100644 index 0000000..1c110a5 --- /dev/null +++ b/crates/recorder/src/fetch/mod.rs @@ -0,0 +1,11 @@ +pub mod bytes; +pub mod client; +pub mod core; +pub mod html; +pub mod image; + +pub use core::DEFAULT_HTTP_CLIENT_USER_AGENT; + +pub use bytes::download_bytes; +pub use client::{HttpClient, HttpClientConfig}; +pub use image::download_image; diff --git a/crates/recorder/src/lib.rs b/crates/recorder/src/lib.rs index 997bc8f..5a67965 100644 --- a/crates/recorder/src/lib.rs +++ b/crates/recorder/src/lib.rs @@ -1,14 +1,13 @@ -#![feature(async_closure, duration_constructors)] +#![feature(duration_constructors, assert_matches)] pub mod app; pub mod config; pub mod controllers; pub mod dal; -pub mod downloaders; +pub mod extract; +pub mod fetch; pub mod migrations; pub mod models; -pub mod parsers; -pub mod path; pub mod tasks; pub mod views; pub mod workers; diff --git a/crates/recorder/src/migrations/defs.rs b/crates/recorder/src/migrations/defs.rs index c326ed6..671544b 100644 --- a/crates/recorder/src/migrations/defs.rs +++ b/crates/recorder/src/migrations/defs.rs @@ -1,4 +1,4 @@ -use std::{collections::HashSet, fmt::Display}; +use std::collections::HashSet; use sea_orm::{DeriveIden, Statement}; use sea_orm_migration::prelude::{extension::postgres::IntoTypeRef, *}; @@ -18,17 +18,17 @@ pub enum Subscribers { Pid, DisplayName, DownloaderId, + BangumiConf, } #[derive(DeriveIden)] pub enum Subscriptions { Table, Id, - SubscriberId, DisplayName, + SubscriberId, Category, SourceUrl, - Aggregate, Enabled, } @@ -36,32 +36,61 @@ pub enum Subscriptions { pub enum Bangumi { Table, Id, + MikanBangumiId, DisplayName, SubscriptionId, + RawName, + Season, + SeasonRaw, + Fansub, + MikanFansubId, + Filter, + RssLink, + PosterLink, + SavePath, + Deleted, + Homepage, + Extra, } #[derive(DeriveIden)] pub enum Episodes { Table, Id, + MikanEpisodeId, + RawName, DisplayName, BangumiId, - OutputName, + SubscriptionId, DownloadId, + SavePath, + Resolution, + Season, + SeasonRaw, + Fansub, + PosterLink, + EpisodeIndex, + Homepage, + Subtitle, + Deleted, + Source, + Extra, } #[derive(DeriveIden)] pub enum Downloads { Table, Id, - SubscriptionId, OriginalName, DisplayName, + SubscriptionId, Status, CurrSize, AllSize, Mime, Url, + Homepage, + SavePath, } #[derive(DeriveIden)] @@ -73,7 +102,17 @@ pub enum Downloaders { Password, Username, SubscriberId, - DownloadPath, + SavePath, +} + +macro_rules! create_postgres_enum_for_active_enum { + ($manager: expr, $active_enum: expr, $($enum_value:expr),+) => { + { + use sea_orm::ActiveEnum; + let values = [$($enum_value,)+].map(|v| ActiveEnum::to_value(&v)); + ($manager).create_postgres_enum_for_active_enum($active_enum, values) + } + }; } #[async_trait::async_trait] @@ -151,8 +190,7 @@ pub trait CustomSchemaManagerExt { async fn create_postgres_enum_for_active_enum< E: IntoTypeRef + IntoIden + Send + Clone, - T: Display + Send, - I: IntoIterator + Send, + I: IntoIterator + Send, >( &self, enum_name: E, @@ -161,8 +199,7 @@ pub trait CustomSchemaManagerExt { async fn add_postgres_enum_values_for_active_enum< E: IntoTypeRef + IntoIden + Send + Clone, - T: Display + Send, - I: IntoIterator + Send, + I: IntoIterator + Send, >( &self, enum_name: E, @@ -186,7 +223,7 @@ pub trait CustomSchemaManagerExt { } #[async_trait::async_trait] -impl<'c> CustomSchemaManagerExt for SchemaManager<'c> { +impl CustomSchemaManagerExt for SchemaManager<'_> { async fn create_postgres_auto_update_ts_fn(&self, col_name: &str) -> Result<(), DbErr> { let sql = format!( "CREATE OR REPLACE FUNCTION update_{col_name}_column() RETURNS TRIGGER AS $$ BEGIN \ @@ -239,8 +276,7 @@ impl<'c> CustomSchemaManagerExt for SchemaManager<'c> { async fn create_postgres_enum_for_active_enum< E: IntoTypeRef + IntoIden + Send + Clone, - T: Display + Send, - I: IntoIterator + Send, + I: IntoIterator + Send, >( &self, enum_name: E, @@ -248,10 +284,7 @@ impl<'c> CustomSchemaManagerExt for SchemaManager<'c> { ) -> Result<(), DbErr> { let existed = self.if_postgres_enum_exists(enum_name.clone()).await?; if !existed { - let idents = values - .into_iter() - .map(|v| Alias::new(v.to_string())) - .collect::>(); + let idents = values.into_iter().map(Alias::new).collect::>(); self.create_type(Type::create().as_enum(enum_name).values(idents).to_owned()) .await?; } else { @@ -263,8 +296,7 @@ impl<'c> CustomSchemaManagerExt for SchemaManager<'c> { async fn add_postgres_enum_values_for_active_enum< E: IntoTypeRef + IntoIden + Send + Clone, - T: Display + Send, - I: IntoIterator + Send, + I: IntoIterator + Send, >( &self, enum_name: E, @@ -273,7 +305,7 @@ impl<'c> CustomSchemaManagerExt for SchemaManager<'c> { let exists_values = self.get_postgres_enum_values(enum_name.clone()).await?; let to_add_values = values .into_iter() - .filter(|v| !exists_values.contains(&v.to_string())) + .filter(|v| !exists_values.contains(v as &str)) .collect::>(); if to_add_values.is_empty() { @@ -283,7 +315,7 @@ impl<'c> CustomSchemaManagerExt for SchemaManager<'c> { let mut type_alter = Type::alter().name(enum_name); for v in to_add_values { - type_alter = type_alter.add_value(Alias::new(v.to_string())); + type_alter = type_alter.add_value(Alias::new(v)); } self.alter_type(type_alter.to_owned()).await?; @@ -294,8 +326,10 @@ impl<'c> CustomSchemaManagerExt for SchemaManager<'c> { &self, enum_name: E, ) -> Result<(), DbErr> { - self.drop_type(Type::drop().name(enum_name).to_owned()) - .await?; + if self.if_postgres_enum_exists(enum_name.clone()).await? { + self.drop_type(Type::drop().name(enum_name).to_owned()) + .await?; + } Ok(()) } diff --git a/crates/recorder/src/migrations/m20220101_000001_init.rs b/crates/recorder/src/migrations/m20220101_000001_init.rs index ac12ab2..b12ecac 100644 --- a/crates/recorder/src/migrations/m20220101_000001_init.rs +++ b/crates/recorder/src/migrations/m20220101_000001_init.rs @@ -1,9 +1,13 @@ +use loco_rs::schema::jsonb_null; use sea_orm_migration::{prelude::*, schema::*}; use super::defs::{ Bangumi, CustomSchemaManagerExt, Episodes, GeneralIds, Subscribers, Subscriptions, }; -use crate::models::{subscribers::ROOT_SUBSCRIBER, subscriptions}; +use crate::models::{ + subscribers::ROOT_SUBSCRIBER, + subscriptions::{self, SubscriptionCategoryEnum}, +}; #[derive(DeriveMigrationName)] pub struct Migration; @@ -14,15 +18,18 @@ impl MigrationTrait for Migration { manager .create_postgres_auto_update_ts_fn_for_col(GeneralIds::UpdatedAt) .await?; + manager .create_table( table_auto(Subscribers::Table) .col(pk_auto(Subscribers::Id)) .col(string_len_uniq(Subscribers::Pid, 64)) .col(string(Subscribers::DisplayName)) + .col(jsonb_null(Subscribers::BangumiConf)) .to_owned(), ) .await?; + manager .create_postgres_auto_update_ts_trigger_for_col( Subscribers::Table, @@ -37,15 +44,13 @@ impl MigrationTrait for Migration { .to_owned(); manager.exec_stmt(insert).await?; - manager - .create_postgres_enum_for_active_enum( - subscriptions::SubscriptionCategoryEnum, - &[ - subscriptions::SubscriptionCategory::Mikan, - subscriptions::SubscriptionCategory::Manual, - ], - ) - .await?; + create_postgres_enum_for_active_enum!( + manager, + subscriptions::SubscriptionCategoryEnum, + subscriptions::SubscriptionCategory::Mikan, + subscriptions::SubscriptionCategory::Manual + ) + .await?; manager .create_table( @@ -54,7 +59,6 @@ impl MigrationTrait for Migration { .col(string(Subscriptions::DisplayName)) .col(integer(Subscriptions::SubscriberId)) .col(text(Subscriptions::SourceUrl)) - .col(boolean(Subscriptions::Aggregate)) .col(boolean(Subscriptions::Enabled)) .col(enumeration( Subscriptions::Category, @@ -63,7 +67,7 @@ impl MigrationTrait for Migration { )) .foreign_key( ForeignKey::create() - .name("fk_subscription_subscriber_id") + .name("fk_subscriptions_subscriber_id") .from(Subscriptions::Table, Subscriptions::SubscriberId) .to(Subscribers::Table, Subscribers::Id) .on_update(ForeignKeyAction::Restrict) @@ -84,8 +88,21 @@ impl MigrationTrait for Migration { .create_table( table_auto(Bangumi::Table) .col(pk_auto(Bangumi::Id)) - .col(text(Bangumi::DisplayName)) + .col(text_null(Bangumi::MikanBangumiId)) .col(integer(Bangumi::SubscriptionId)) + .col(text(Bangumi::DisplayName)) + .col(text(Bangumi::RawName)) + .col(integer(Bangumi::Season)) + .col(text_null(Bangumi::SeasonRaw)) + .col(text_null(Bangumi::Fansub)) + .col(text_null(Bangumi::MikanFansubId)) + .col(jsonb_null(Bangumi::Filter)) + .col(text_null(Bangumi::RssLink)) + .col(text_null(Bangumi::PosterLink)) + .col(text_null(Bangumi::SavePath)) + .col(boolean(Bangumi::Deleted).default(false)) + .col(text_null(Bangumi::Homepage)) + .col(jsonb_null(Bangumi::Extra)) .foreign_key( ForeignKey::create() .name("fk_bangumi_subscription_id") @@ -98,6 +115,28 @@ impl MigrationTrait for Migration { ) .await?; + manager + .create_index( + Index::create() + .if_not_exists() + .name("idx_bangumi_mikan_bangumi_id") + .table(Bangumi::Table) + .col(Bangumi::MikanBangumiId) + .to_owned(), + ) + .await?; + + manager + .create_index( + Index::create() + .if_not_exists() + .name("idx_bangumi_mikan_fansub_id") + .table(Bangumi::Table) + .col(Bangumi::MikanFansubId) + .to_owned(), + ) + .await?; + manager .create_postgres_auto_update_ts_trigger_for_col(Bangumi::Table, GeneralIds::UpdatedAt) .await?; @@ -106,12 +145,34 @@ impl MigrationTrait for Migration { .create_table( table_auto(Episodes::Table) .col(pk_auto(Episodes::Id)) + .col(text_null(Episodes::MikanEpisodeId)) + .col(text(Episodes::RawName)) .col(text(Episodes::DisplayName)) .col(integer(Episodes::BangumiId)) - .col(text(Episodes::OutputName)) + .col(integer(Episodes::SubscriptionId)) + .col(text_null(Episodes::SavePath)) + .col(text_null(Episodes::Resolution)) + .col(integer(Episodes::Season)) + .col(text_null(Episodes::SeasonRaw)) + .col(text_null(Episodes::Fansub)) + .col(text_null(Episodes::PosterLink)) + .col(integer(Episodes::EpisodeIndex)) + .col(text_null(Episodes::Homepage)) + .col(text_null(Episodes::Subtitle)) + .col(boolean(Episodes::Deleted).default(false)) + .col(text_null(Episodes::Source)) + .col(jsonb_null(Episodes::Extra)) .foreign_key( ForeignKey::create() - .name("fk_episode_bangumi_id") + .name("fk_episodes_subscription_id") + .from(Episodes::Table, Episodes::SubscriptionId) + .to(Subscriptions::Table, Subscriptions::Id) + .on_update(ForeignKeyAction::Restrict) + .on_delete(ForeignKeyAction::Cascade), + ) + .foreign_key( + ForeignKey::create() + .name("fk_episodes_bangumi_id") .from(Episodes::Table, Episodes::BangumiId) .to(Bangumi::Table, Bangumi::Id) .on_update(ForeignKeyAction::Restrict) @@ -121,6 +182,30 @@ impl MigrationTrait for Migration { ) .await?; + manager + .create_index( + Index::create() + .if_not_exists() + .name("idx_episodes_mikan_episode_id") + .table(Episodes::Table) + .col(Episodes::MikanEpisodeId) + .to_owned(), + ) + .await?; + + manager + .create_index( + Index::create() + .if_not_exists() + .name("idx_episodes_bangumi_id_mikan_episode_id") + .table(Episodes::Table) + .col(Episodes::BangumiId) + .col(Episodes::MikanEpisodeId) + .unique() + .to_owned(), + ) + .await?; + manager .create_postgres_auto_update_ts_trigger_for_col(Episodes::Table, GeneralIds::UpdatedAt) .await?; @@ -129,18 +214,24 @@ impl MigrationTrait for Migration { } async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(Episodes::Table).to_owned()) + .await?; + manager .drop_postgres_auto_update_ts_trigger_for_col(Episodes::Table, GeneralIds::UpdatedAt) .await?; + manager - .drop_table(Table::drop().table(Episodes::Table).to_owned()) + .drop_table(Table::drop().table(Bangumi::Table).to_owned()) .await?; manager .drop_postgres_auto_update_ts_trigger_for_col(Bangumi::Table, GeneralIds::UpdatedAt) .await?; + manager - .drop_table(Table::drop().table(Bangumi::Table).to_owned()) + .drop_table(Table::drop().table(Subscriptions::Table).to_owned()) .await?; manager @@ -149,8 +240,9 @@ impl MigrationTrait for Migration { GeneralIds::UpdatedAt, ) .await?; + manager - .drop_table(Table::drop().table(Subscriptions::Table).to_owned()) + .drop_table(Table::drop().table(Subscribers::Table).to_owned()) .await?; manager @@ -158,11 +250,15 @@ impl MigrationTrait for Migration { .await?; manager - .drop_table(Table::drop().table(Subscribers::Table).to_owned()) + .drop_postgres_enum_for_active_enum(subscriptions::SubscriptionCategoryEnum) .await?; manager - .drop_postgres_enum_for_active_enum(subscriptions::SubscriptionCategoryEnum) + .drop_postgres_auto_update_ts_fn_for_col(GeneralIds::UpdatedAt) + .await?; + + manager + .drop_postgres_enum_for_active_enum(SubscriptionCategoryEnum) .await?; Ok(()) diff --git a/crates/recorder/src/migrations/m20240224_082543_add_downloads.rs b/crates/recorder/src/migrations/m20240224_082543_add_downloads.rs index 54fe73b..2f39eba 100644 --- a/crates/recorder/src/migrations/m20240224_082543_add_downloads.rs +++ b/crates/recorder/src/migrations/m20240224_082543_add_downloads.rs @@ -13,26 +13,25 @@ pub struct Migration; #[async_trait::async_trait] impl MigrationTrait for Migration { async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .create_postgres_enum_for_active_enum( - DownloadMimeEnum, - &[DownloadMime::OctetStream, DownloadMime::BitTorrent], - ) - .await?; + create_postgres_enum_for_active_enum!( + manager, + DownloadMimeEnum, + DownloadMime::BitTorrent, + DownloadMime::OctetStream + ) + .await?; - manager - .create_postgres_enum_for_active_enum( - DownloadStatusEnum, - &[ - DownloadStatus::Pending, - DownloadStatus::Downloading, - DownloadStatus::Completed, - DownloadStatus::Failed, - DownloadStatus::Deleted, - DownloadStatus::Paused, - ], - ) - .await?; + create_postgres_enum_for_active_enum!( + manager, + DownloadStatusEnum, + DownloadStatus::Downloading, + DownloadStatus::Paused, + DownloadStatus::Pending, + DownloadStatus::Completed, + DownloadStatus::Failed, + DownloadStatus::Deleted + ) + .await?; manager .create_table( @@ -54,15 +53,11 @@ impl MigrationTrait for Migration { .col(big_unsigned(Downloads::AllSize)) .col(big_unsigned(Downloads::CurrSize)) .col(text(Downloads::Url)) - .index( - Index::create() - .table(Downloads::Table) - .col(Downloads::Url) - .name("idx_download_url"), - ) + .col(text_null(Downloads::Homepage)) + .col(text_null(Downloads::SavePath)) .foreign_key( ForeignKey::create() - .name("fk_download_subscription_id") + .name("fk_downloads_subscription_id") .from(Downloads::Table, Downloads::SubscriptionId) .to(Subscriptions::Table, Subscriptions::Id) .on_update(ForeignKeyAction::Restrict) @@ -73,7 +68,14 @@ impl MigrationTrait for Migration { .await?; manager - .create_postgres_auto_update_ts_fn_for_col(GeneralIds::UpdatedAt) + .create_index( + Index::create() + .if_not_exists() + .name("idx_downloads_url") + .table(Downloads::Table) + .col(Downloads::Url) + .to_owned(), + ) .await?; manager @@ -83,7 +85,7 @@ impl MigrationTrait for Migration { .add_column_if_not_exists(integer_null(Episodes::DownloadId)) .add_foreign_key( TableForeignKey::new() - .name("fk_episode_download_id") + .name("fk_episodes_download_id") .from_tbl(Episodes::Table) .from_col(Episodes::DownloadId) .to_tbl(Downloads::Table) @@ -103,16 +105,12 @@ impl MigrationTrait for Migration { .alter_table( Table::alter() .table(Episodes::Table) - .drop_foreign_key(Alias::new("fk_episode_download_id")) + .drop_foreign_key(Alias::new("fk_episodes_download_id")) .drop_column(Episodes::DownloadId) .to_owned(), ) .await?; - manager - .drop_postgres_auto_update_ts_fn_for_col(GeneralIds::UpdatedAt) - .await?; - manager .drop_table(Table::drop().table(Downloads::Table).to_owned()) .await?; @@ -120,6 +118,7 @@ impl MigrationTrait for Migration { manager .drop_postgres_enum_for_active_enum(DownloadMimeEnum) .await?; + manager .drop_postgres_enum_for_active_enum(DownloadStatusEnum) .await?; diff --git a/crates/recorder/src/migrations/m20240225_060853_subscriber_add_downloader.rs b/crates/recorder/src/migrations/m20240225_060853_subscriber_add_downloader.rs index d969a61..47df3a8 100644 --- a/crates/recorder/src/migrations/m20240225_060853_subscriber_add_downloader.rs +++ b/crates/recorder/src/migrations/m20240225_060853_subscriber_add_downloader.rs @@ -11,12 +11,12 @@ pub struct Migration; #[async_trait::async_trait] impl MigrationTrait for Migration { async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .create_postgres_enum_for_active_enum( - DownloaderCategoryEnum, - &[DownloaderCategory::QBittorrent], - ) - .await?; + create_postgres_enum_for_active_enum!( + manager, + DownloaderCategoryEnum, + DownloaderCategory::QBittorrent + ) + .await?; manager .create_table( @@ -30,7 +30,7 @@ impl MigrationTrait for Migration { DownloaderCategoryEnum, DownloaderCategory::iden_values(), )) - .col(text(Downloaders::DownloadPath)) + .col(text(Downloaders::SavePath)) .col(integer(Downloaders::SubscriberId)) .foreign_key( ForeignKey::create() @@ -60,7 +60,7 @@ impl MigrationTrait for Migration { .add_column_if_not_exists(integer_null(Subscribers::DownloaderId)) .add_foreign_key( TableForeignKey::new() - .name("fk_subscriber_downloader_id") + .name("fk_subscribers_downloader_id") .from_tbl(Subscribers::Table) .from_col(Subscribers::DownloaderId) .to_tbl(Downloaders::Table) @@ -79,7 +79,7 @@ impl MigrationTrait for Migration { .alter_table( Table::alter() .table(Subscribers::Table) - .drop_foreign_key(Alias::new("fk_subscriber_downloader_id")) + .drop_foreign_key(Alias::new("fk_subscribers_downloader_id")) .drop_column(Subscribers::DownloaderId) .to_owned(), ) diff --git a/crates/recorder/src/migrations/mod.rs b/crates/recorder/src/migrations/mod.rs index 4c31493..c26c4bd 100644 --- a/crates/recorder/src/migrations/mod.rs +++ b/crates/recorder/src/migrations/mod.rs @@ -1,5 +1,6 @@ pub use sea_orm_migration::prelude::*; +#[macro_use] pub mod defs; pub mod m20220101_000001_init; pub mod m20240224_082543_add_downloads; diff --git a/crates/recorder/src/models/bangumi.rs b/crates/recorder/src/models/bangumi.rs index c4d1c37..af30fc4 100644 --- a/crates/recorder/src/models/bangumi.rs +++ b/crates/recorder/src/models/bangumi.rs @@ -1,6 +1,43 @@ -use sea_orm::entity::prelude::*; +use loco_rs::app::AppContext; +use sea_orm::{entity::prelude::*, ActiveValue, TryIntoModel}; pub use super::entities::bangumi::*; +impl Model { + pub async fn get_or_insert_from_mikan( + ctx: &AppContext, + subscription_id: i32, + mikan_bangumi_id: String, + mikan_fansub_id: String, + f: F, + ) -> eyre::Result + where + F: AsyncFnOnce(&mut ActiveModel) -> eyre::Result<()>, + { + let db = &ctx.db; + if let Some(existed) = Entity::find() + .filter( + Column::MikanBangumiId + .eq(Some(mikan_bangumi_id.clone())) + .and(Column::MikanFansubId.eq(Some(mikan_fansub_id.clone()))), + ) + .one(db) + .await? + { + Ok(existed) + } else { + let mut bgm = ActiveModel { + mikan_bangumi_id: ActiveValue::Set(Some(mikan_bangumi_id)), + mikan_fansub_id: ActiveValue::Set(Some(mikan_fansub_id)), + subscription_id: ActiveValue::Set(subscription_id), + ..Default::default() + }; + f(&mut bgm).await?; + let bgm: Model = bgm.save(db).await?.try_into_model()?; + Ok(bgm) + } + } +} + #[async_trait::async_trait] impl ActiveModelBehavior for ActiveModel {} diff --git a/crates/recorder/src/models/downloaders.rs b/crates/recorder/src/models/downloaders.rs index 5b1de40..5fc35aa 100644 --- a/crates/recorder/src/models/downloaders.rs +++ b/crates/recorder/src/models/downloaders.rs @@ -7,6 +7,9 @@ pub use crate::models::entities::downloaders::*; impl ActiveModelBehavior for ActiveModel {} impl Model { + pub fn get_endpoint(&self) -> String { + self.endpoint.clone() + } pub fn endpoint_url(&self) -> Result { let url = Url::parse(&self.endpoint)?; Ok(url) diff --git a/crates/recorder/src/models/downloads.rs b/crates/recorder/src/models/downloads.rs index c82ae89..b7ccfa6 100644 --- a/crates/recorder/src/models/downloads.rs +++ b/crates/recorder/src/models/downloads.rs @@ -1,11 +1,7 @@ -use loco_rs::app::AppContext; -use sea_orm::{prelude::*, sea_query::OnConflict, ActiveValue, Condition, QueryOrder, QuerySelect}; +use sea_orm::{prelude::*, ActiveValue}; +use crate::extract::mikan::MikanRssItem; pub use crate::models::entities::downloads::*; -use crate::{ - models::subscriptions::{self, SubscriptionCategory}, - parsers::mikan::{parse_mikan_rss_items_from_rss_link, MikanRssItem}, -}; #[async_trait::async_trait] impl ActiveModelBehavior for ActiveModel {} @@ -18,73 +14,14 @@ impl ActiveModel { subscription_id: ActiveValue::Set(subscription_id), status: ActiveValue::Set(DownloadStatus::Pending), mime: ActiveValue::Set(DownloadMime::BitTorrent), - url: ActiveValue::Set(m.url), + url: ActiveValue::Set(m.url.to_string()), curr_size: ActiveValue::Set(m.content_length.as_ref().map(|_| 0)), all_size: ActiveValue::Set(m.content_length), - homepage: ActiveValue::Set(m.homepage), + homepage: ActiveValue::Set(Some(m.homepage.to_string())), ..Default::default() }; todo!() } } -impl Model { - pub async fn pull_subscription( - ctx: AppContext, - item: &subscriptions::Model, - ) -> eyre::Result> { - let db = &ctx.db; - match &item.category { - SubscriptionCategory::Mikan => { - let items = parse_mikan_rss_items_from_rss_link(&item.source_url).await?; - let all_items = items.collect::>(); - - let last_old_id = { - Entity::find() - .select_only() - .column(Column::Id) - .order_by_desc(Column::Id) - .filter(Column::SubscriptionId.eq(item.id)) - .one(db) - .await? - } - .map(|i| i.id); - - if all_items.is_empty() { - return Ok(vec![]); - } - - let new_items = all_items - .into_iter() - .map(|i| ActiveModel::from_mikan_rss_item(i, item.id)); - - let insert_result = Entity::insert_many(new_items) - .on_conflict(OnConflict::column(Column::Url).do_nothing().to_owned()) - .exec(db) - .await?; - - let insert_ids = Entity::find() - .select_only() - .column(Column::Id) - .filter({ - let mut cond = Condition::all() - .add(Column::SubscriptionId.eq(item.id)) - .add(Column::Id.lte(insert_result.last_insert_id)); - - if let Some(last_old_id) = last_old_id { - cond = cond.add(Column::Id.gt(last_old_id)) - } - - cond - }) - .all(db) - .await?; - - Ok(insert_ids.into_iter().map(|i| i.id).collect::>()) - } - _ => { - todo!("other subscription categories") - } - } - } -} +impl Model {} diff --git a/crates/recorder/src/models/entities/bangumi.rs b/crates/recorder/src/models/entities/bangumi.rs index 07fcd93..23e2b19 100644 --- a/crates/recorder/src/models/entities/bangumi.rs +++ b/crates/recorder/src/models/entities/bangumi.rs @@ -7,6 +7,16 @@ pub struct BangumiFilter { pub group: Option>, } +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, FromJsonQueryResult)] +pub struct BangumiExtra { + pub name_zh: Option, + pub s_name_zh: Option, + pub name_en: Option, + pub s_name_en: Option, + pub name_jp: Option, + pub s_name_jp: Option, +} + #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)] #[sea_orm(table_name = "bangumi")] pub struct Model { @@ -14,17 +24,22 @@ pub struct Model { pub updated_at: DateTime, #[sea_orm(primary_key)] pub id: i32, + pub mikan_bangumi_id: Option, pub subscription_id: i32, pub display_name: String, - pub official_title: String, + pub raw_name: String, pub season: i32, pub season_raw: Option, pub fansub: Option, + pub mikan_fansub_id: Option, pub filter: Option, pub rss_link: Option, pub poster_link: Option, pub save_path: Option, + #[sea_orm(default = "false")] pub deleted: bool, + pub homepage: Option, + pub extra: Option, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] diff --git a/crates/recorder/src/models/entities/downloads.rs b/crates/recorder/src/models/entities/downloads.rs index dd34bce..f0bb76b 100644 --- a/crates/recorder/src/models/entities/downloads.rs +++ b/crates/recorder/src/models/entities/downloads.rs @@ -50,6 +50,7 @@ pub struct Model { pub all_size: Option, pub curr_size: Option, pub homepage: Option, + pub save_path: Option, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] diff --git a/crates/recorder/src/models/entities/episodes.rs b/crates/recorder/src/models/entities/episodes.rs index 2c0ae43..ad85b6d 100644 --- a/crates/recorder/src/models/entities/episodes.rs +++ b/crates/recorder/src/models/entities/episodes.rs @@ -1,8 +1,17 @@ //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.2 - -use sea_orm::entity::prelude::*; +use sea_orm::{entity::prelude::*, FromJsonQueryResult}; use serde::{Deserialize, Serialize}; +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, FromJsonQueryResult, Default)] +pub struct EpisodeExtra { + pub name_zh: Option, + pub s_name_zh: Option, + pub name_en: Option, + pub s_name_en: Option, + pub name_jp: Option, + pub s_name_jp: Option, +} + #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)] #[sea_orm(table_name = "episodes")] pub struct Model { @@ -10,27 +19,26 @@ pub struct Model { pub updated_at: DateTime, #[sea_orm(primary_key)] pub id: i32, + #[sea_orm(indexed)] + pub mikan_episode_id: Option, pub raw_name: String, - pub official_title: String, pub display_name: String, - pub name_zh: Option, - pub name_jp: Option, - pub name_en: Option, - pub s_name_zh: Option, - pub s_name_jp: Option, - pub s_name_en: Option, pub bangumi_id: i32, - pub download_id: i32, - pub save_path: String, + pub subscription_id: i32, + pub download_id: Option, + pub save_path: Option, pub resolution: Option, pub season: i32, pub season_raw: Option, pub fansub: Option, pub poster_link: Option, - pub home_page: Option, + pub episode_index: i32, + pub homepage: Option, pub subtitle: Option>, + #[sea_orm(default = "false")] pub deleted: bool, pub source: Option, + pub extra: EpisodeExtra, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] @@ -47,6 +55,12 @@ pub enum Relation { to = "super::downloads::Column::Id" )] Downloads, + #[sea_orm( + belongs_to = "super::subscriptions::Entity", + from = "Column::SubscriptionId", + to = "super::subscriptions::Column::Id" + )] + Subscriptions, } impl Related for Entity { @@ -60,3 +74,9 @@ impl Related for Entity { Relation::Downloads.def() } } + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Subscriptions.def() + } +} diff --git a/crates/recorder/src/models/entities/subscribers.rs b/crates/recorder/src/models/entities/subscribers.rs index 1eef889..af3620e 100644 --- a/crates/recorder/src/models/entities/subscribers.rs +++ b/crates/recorder/src/models/entities/subscribers.rs @@ -19,7 +19,7 @@ pub struct Model { pub pid: String, pub display_name: String, pub downloader_id: Option, - pub bangumi_conf: SubscriberBangumiConfig, + pub bangumi_conf: Option, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] diff --git a/crates/recorder/src/models/entities/subscriptions.rs b/crates/recorder/src/models/entities/subscriptions.rs index 8fee5f2..eba5e65 100644 --- a/crates/recorder/src/models/entities/subscriptions.rs +++ b/crates/recorder/src/models/entities/subscriptions.rs @@ -30,7 +30,6 @@ pub struct Model { pub subscriber_id: i32, pub category: SubscriptionCategory, pub source_url: String, - pub aggregate: bool, pub enabled: bool, } @@ -44,6 +43,8 @@ pub enum Relation { Subscriber, #[sea_orm(has_many = "super::bangumi::Entity")] Bangumi, + #[sea_orm(has_many = "super::episodes::Entity")] + Episodes, } impl Related for Entity { @@ -57,3 +58,9 @@ impl Related for Entity { Relation::Bangumi.def() } } + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Episodes.def() + } +} diff --git a/crates/recorder/src/models/episodes.rs b/crates/recorder/src/models/episodes.rs index 00c1435..49602c8 100644 --- a/crates/recorder/src/models/episodes.rs +++ b/crates/recorder/src/models/episodes.rs @@ -1,6 +1,91 @@ -use sea_orm::entity::prelude::*; +use std::sync::Arc; +use loco_rs::app::AppContext; +use sea_orm::{entity::prelude::*, sea_query::OnConflict, ActiveValue}; + +use super::bangumi; pub use super::entities::episodes::*; +use crate::{ + app::AppContextExt, + extract::{ + mikan::{build_mikan_episode_homepage, MikanEpisodeMeta}, + rawname::parse_episode_meta_from_raw_name, + }, +}; + +#[derive(Clone, Debug, PartialEq)] +pub struct MikanEpsiodeCreation { + pub episode: MikanEpisodeMeta, + pub bangumi: Arc, +} + +impl Model { + pub async fn add_episodes( + ctx: &AppContext, + creations: impl IntoIterator, + ) -> eyre::Result<()> { + let db = &ctx.db; + let new_episode_active_modes = creations + .into_iter() + .flat_map(|cr| ActiveModel::from_mikan_episode_meta(ctx, cr)); + + Entity::insert_many(new_episode_active_modes) + .on_conflict( + OnConflict::columns([Column::BangumiId, Column::MikanEpisodeId]) + .do_nothing() + .to_owned(), + ) + .exec(db) + .await?; + + Ok(()) + } +} + +impl ActiveModel { + pub fn from_mikan_episode_meta( + ctx: &AppContext, + creation: MikanEpsiodeCreation, + ) -> eyre::Result { + let item = creation.episode; + let bgm = creation.bangumi; + let raw_meta = parse_episode_meta_from_raw_name(&item.episode_title)?; + let homepage = build_mikan_episode_homepage( + ctx.get_mikan_client().base_url(), + &item.mikan_episode_id, + )?; + + Ok(Self { + mikan_episode_id: ActiveValue::Set(Some(item.mikan_episode_id)), + raw_name: ActiveValue::Set(item.episode_title.clone()), + display_name: ActiveValue::Set(item.episode_title.clone()), + bangumi_id: ActiveValue::Set(bgm.id), + subscription_id: ActiveValue::Set(bgm.subscription_id), + resolution: ActiveValue::Set(raw_meta.resolution), + season: ActiveValue::Set(if raw_meta.season > 0 { + raw_meta.season + } else { + bgm.season + }), + season_raw: ActiveValue::Set(raw_meta.season_raw.or_else(|| bgm.season_raw.clone())), + fansub: ActiveValue::Set(raw_meta.fansub.or_else(|| bgm.fansub.clone())), + poster_link: ActiveValue::Set(bgm.poster_link.clone()), + episode_index: ActiveValue::Set(raw_meta.episode_index), + homepage: ActiveValue::Set(Some(homepage.to_string())), + subtitle: ActiveValue::Set(raw_meta.subtitle.map(|s| vec![s])), + source: ActiveValue::Set(raw_meta.source), + extra: ActiveValue::Set(EpisodeExtra { + name_zh: raw_meta.name_zh, + name_en: raw_meta.name_en, + name_jp: raw_meta.name_jp, + s_name_en: raw_meta.name_en_no_season, + s_name_jp: raw_meta.name_jp_no_season, + s_name_zh: raw_meta.name_zh_no_season, + }), + ..Default::default() + }) + } +} #[async_trait::async_trait] impl ActiveModelBehavior for ActiveModel {} diff --git a/crates/recorder/src/models/mod.rs b/crates/recorder/src/models/mod.rs index 9c2dc99..d0dd646 100644 --- a/crates/recorder/src/models/mod.rs +++ b/crates/recorder/src/models/mod.rs @@ -5,5 +5,6 @@ pub mod entities; pub mod episodes; pub mod notifications; pub mod prelude; +pub mod query; pub mod subscribers; pub mod subscriptions; diff --git a/crates/recorder/src/models/notifications.rs b/crates/recorder/src/models/notifications.rs index 8c0e7af..f52d531 100644 --- a/crates/recorder/src/models/notifications.rs +++ b/crates/recorder/src/models/notifications.rs @@ -2,7 +2,6 @@ use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct Notification { - official_title: String, season: i32, episode_size: u32, poster_url: Option, diff --git a/crates/recorder/src/models/query/mod.rs b/crates/recorder/src/models/query/mod.rs new file mode 100644 index 0000000..5fa270e --- /dev/null +++ b/crates/recorder/src/models/query/mod.rs @@ -0,0 +1,26 @@ +use sea_orm::{ + prelude::Expr, + sea_query::{Alias, IntoColumnRef, IntoTableRef, Query, SelectStatement}, + Value, +}; + +pub fn filter_values_in< + I: IntoIterator, + T: Into, + R: IntoTableRef, + C: IntoColumnRef + Copy, +>( + tbl_ref: R, + col_ref: C, + values: I, +) -> SelectStatement { + Query::select() + .expr(Expr::col((Alias::new("t"), Alias::new("column1")))) + .from_values(values, Alias::new("t")) + .left_join( + tbl_ref, + Expr::col((Alias::new("t"), Alias::new("column1"))).equals(col_ref), + ) + .and_where(Expr::col(col_ref).is_not_null()) + .to_owned() +} diff --git a/crates/recorder/src/models/subscribers.rs b/crates/recorder/src/models/subscribers.rs index 84cd0ee..5d4da2c 100644 --- a/crates/recorder/src/models/subscribers.rs +++ b/crates/recorder/src/models/subscribers.rs @@ -1,4 +1,7 @@ -use loco_rs::model::{ModelError, ModelResult}; +use loco_rs::{ + app::AppContext, + model::{ModelError, ModelResult}, +}; use sea_orm::{entity::prelude::*, ActiveValue, TransactionTrait}; use serde::{Deserialize, Serialize}; @@ -33,7 +36,8 @@ impl Model { /// # Errors /// /// When could not find user or DB query error - pub async fn find_by_pid(db: &DatabaseConnection, pid: &str) -> ModelResult { + pub async fn find_by_pid(ctx: &AppContext, pid: &str) -> ModelResult { + let db = &ctx.db; let parse_uuid = Uuid::parse_str(pid).map_err(|e| ModelError::Any(e.into()))?; let subscriber = Entity::find() .filter(Column::Pid.eq(parse_uuid)) @@ -42,8 +46,30 @@ impl Model { subscriber.ok_or_else(|| ModelError::EntityNotFound) } - pub async fn find_root(db: &DatabaseConnection) -> ModelResult { - Self::find_by_pid(db, ROOT_SUBSCRIBER).await + pub async fn find_by_id(ctx: &AppContext, id: i32) -> ModelResult { + let db = &ctx.db; + + let subscriber = Entity::find_by_id(id).one(db).await?; + subscriber.ok_or_else(|| ModelError::EntityNotFound) + } + + pub async fn find_pid_by_id_with_cache(ctx: &AppContext, id: i32) -> eyre::Result { + let db = &ctx.db; + let cache = &ctx.cache; + let pid = cache + .get_or_insert(&format!("subscriber-id2pid::{}", id), async { + let subscriber = Entity::find_by_id(id) + .one(db) + .await? + .ok_or_else(|| loco_rs::Error::string(&format!("No such pid for id {}", id)))?; + Ok(subscriber.pid) + }) + .await?; + Ok(pid) + } + + pub async fn find_root(ctx: &AppContext) -> ModelResult { + Self::find_by_pid(ctx, ROOT_SUBSCRIBER).await } /// Asynchronously creates a user with a password and saves it to the @@ -52,7 +78,8 @@ impl Model { /// # Errors /// /// When could not save the user into the DB - pub async fn create_root(db: &DatabaseConnection) -> ModelResult { + pub async fn create_root(ctx: &AppContext) -> ModelResult { + let db = &ctx.db; let txn = db.begin().await?; let user = ActiveModel { diff --git a/crates/recorder/src/models/subscriptions.rs b/crates/recorder/src/models/subscriptions.rs index 2da4abb..3b85a94 100644 --- a/crates/recorder/src/models/subscriptions.rs +++ b/crates/recorder/src/models/subscriptions.rs @@ -1,13 +1,33 @@ +use std::{collections::HashSet, sync::Arc}; + +use itertools::Itertools; +use loco_rs::app::AppContext; use sea_orm::{entity::prelude::*, ActiveValue}; use serde::{Deserialize, Serialize}; pub use super::entities::subscriptions::{self, *}; +use super::{bangumi, episodes, query::filter_values_in}; +use crate::{ + app::AppContextExt, + extract::{ + mikan::{ + build_mikan_bangumi_homepage, build_mikan_bangumi_rss_link, + parse_mikan_bangumi_meta_from_mikan_homepage, + parse_mikan_episode_meta_from_mikan_homepage, parse_mikan_rss_channel_from_rss_link, + web_parser::{ + parse_mikan_bangumi_poster_from_origin_poster_src_with_cache, + MikanBangumiPosterMeta, + }, + }, + rawname::extract_season_from_title_body, + }, + models::episodes::MikanEpsiodeCreation, +}; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct SubscriptionCreateFromRssDto { pub rss_link: String, pub display_name: String, - pub aggregate: bool, pub enabled: Option, } @@ -37,7 +57,6 @@ impl ActiveModel { Self { display_name: ActiveValue::Set(create_dto.display_name), enabled: ActiveValue::Set(create_dto.enabled.unwrap_or(false)), - aggregate: ActiveValue::Set(create_dto.aggregate), subscriber_id: ActiveValue::Set(subscriber_id), category: ActiveValue::Set(category), source_url: ActiveValue::Set(create_dto.rss_link), @@ -48,20 +67,22 @@ impl ActiveModel { impl Model { pub async fn add_subscription( - db: &DatabaseConnection, + ctx: &AppContext, create_dto: SubscriptionCreateDto, subscriber_id: i32, ) -> eyre::Result { + let db = &ctx.db; let subscription = ActiveModel::from_create_dto(create_dto, subscriber_id); Ok(subscription.insert(db).await?) } pub async fn toggle_iters( - db: &DatabaseConnection, + ctx: &AppContext, ids: impl Iterator, enabled: bool, ) -> eyre::Result<()> { + let db = &ctx.db; Entity::update_many() .col_expr(Column::Enabled, Expr::value(enabled)) .filter(Column::Id.is_in(ids)) @@ -71,13 +92,137 @@ impl Model { } pub async fn delete_iters( - db: &DatabaseConnection, + ctx: &AppContext, ids: impl Iterator, ) -> eyre::Result<()> { + let db = &ctx.db; Entity::delete_many() .filter(Column::Id.is_in(ids)) .exec(db) .await?; Ok(()) } + + pub async fn pull_subscription(&self, ctx: &AppContext) -> eyre::Result<()> { + match &self.category { + SubscriptionCategory::Mikan => { + let mikan_client = ctx.get_mikan_client(); + let channel = + parse_mikan_rss_channel_from_rss_link(Some(mikan_client), &self.source_url) + .await?; + + let items = channel.into_items(); + + let db = &ctx.db; + let items = items.into_iter().collect_vec(); + + let mut stmt = filter_values_in( + episodes::Entity, + episodes::Column::MikanEpisodeId, + items + .iter() + .map(|s| Value::from(s.mikan_episode_id.clone())), + ); + stmt.expr(Expr::col(episodes::Column::SubscriptionId)) + .and_where(Expr::col(episodes::Column::SubscriptionId).eq(self.id)); + + let builder = &db.get_database_backend(); + + let old_rss_item_mikan_episode_ids_set = db + .query_all(builder.build(&stmt)) + .await? + .into_iter() + .flat_map(|qs| qs.try_get_by_index::(0)) + .collect::>(); + + let new_rss_items = items + .into_iter() + .filter(|item| { + !old_rss_item_mikan_episode_ids_set.contains(&item.mikan_episode_id) + }) + .collect_vec(); + + let mut new_metas = vec![]; + for new_rss_item in new_rss_items.iter() { + new_metas.push( + parse_mikan_episode_meta_from_mikan_homepage( + Some(mikan_client), + new_rss_item.homepage.clone(), + ) + .await?, + ); + } + + let new_mikan_bangumi_groups = new_metas + .into_iter() + .into_group_map_by(|s| (s.mikan_bangumi_id.clone(), s.mikan_fansub_id.clone())); + + for ((mikan_bangumi_id, mikan_fansub_id), new_ep_metas) in new_mikan_bangumi_groups + { + let mikan_base_url = ctx.get_mikan_client().base_url(); + let bgm_homepage = build_mikan_bangumi_homepage( + mikan_base_url, + &mikan_bangumi_id, + Some(&mikan_fansub_id), + )?; + let bgm_rss_link = build_mikan_bangumi_rss_link( + mikan_base_url, + &mikan_bangumi_id, + Some(&mikan_fansub_id), + )?; + let bgm = Arc::new( + bangumi::Model::get_or_insert_from_mikan( + ctx, + self.id, + mikan_bangumi_id.to_string(), + mikan_fansub_id.to_string(), + async |am| -> eyre::Result<()> { + let bgm_meta = parse_mikan_bangumi_meta_from_mikan_homepage( + Some(mikan_client), + bgm_homepage.clone(), + ) + .await?; + let bgm_name = bgm_meta.bangumi_title; + let (_, bgm_season_raw, bgm_season) = + extract_season_from_title_body(&bgm_name); + am.raw_name = ActiveValue::Set(bgm_name.clone()); + am.display_name = ActiveValue::Set(bgm_name); + am.season = ActiveValue::Set(bgm_season); + am.season_raw = ActiveValue::Set(bgm_season_raw); + am.rss_link = ActiveValue::Set(Some(bgm_rss_link.to_string())); + am.homepage = ActiveValue::Set(Some(bgm_homepage.to_string())); + am.fansub = ActiveValue::Set(bgm_meta.fansub); + if let Some(origin_poster_src) = bgm_meta.origin_poster_src { + if let MikanBangumiPosterMeta { + poster_src: Some(poster_src), + .. + } = parse_mikan_bangumi_poster_from_origin_poster_src_with_cache( + ctx, + origin_poster_src, + self.subscriber_id, + ) + .await? + { + am.poster_link = ActiveValue::Set(Some(poster_src)) + } + } + Ok(()) + }, + ) + .await?, + ); + episodes::Model::add_episodes( + ctx, + new_ep_metas.into_iter().map(|item| MikanEpsiodeCreation { + episode: item, + bangumi: bgm.clone(), + }), + ) + .await?; + } + Ok(()) + } + _ => todo!(), + } + } } diff --git a/crates/recorder/src/parsers/html/html_parser_utils.rs b/crates/recorder/src/parsers/html/html_parser_utils.rs deleted file mode 100644 index f9a9539..0000000 --- a/crates/recorder/src/parsers/html/html_parser_utils.rs +++ /dev/null @@ -1,34 +0,0 @@ -use lightningcss::declaration::DeclarationBlock; - -pub fn query_selector_first<'a>( - dom: &'a tl::VDom<'a>, - selector: &'a str, - parser: &'a tl::Parser<'a>, -) -> Option<&'a tl::Node<'a>> { - dom.query_selector(selector) - .and_then(|mut s| s.next()) - .and_then(|n| n.get(parser)) -} - -pub fn query_selector_first_tag<'a>( - dom: &'a tl::VDom<'a>, - selector: &'a str, - parser: &'a tl::Parser<'a>, -) -> Option<&'a tl::HTMLTag<'a>> { - query_selector_first(dom, selector, parser).and_then(|n| n.as_tag()) -} - -pub fn parse_style_attr(style_attr: &str) -> Option { - let result = DeclarationBlock::parse_string(style_attr, Default::default()).ok()?; - Some(result) -} - -pub fn get_tag_style<'a>(tag: &'a tl::HTMLTag<'a>) -> Option> { - let style_attr = tag - .attributes() - .get("style") - .flatten() - .and_then(|s| std::str::from_utf8(s.as_bytes()).ok()); - - style_attr.and_then(parse_style_attr) -} diff --git a/crates/recorder/src/parsers/html/mod.rs b/crates/recorder/src/parsers/html/mod.rs deleted file mode 100644 index 2065643..0000000 --- a/crates/recorder/src/parsers/html/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod html_parser_utils; - -pub use html_parser_utils::{get_tag_style, query_selector_first_tag}; diff --git a/crates/recorder/src/parsers/mikan/mikan_ep_parser.rs b/crates/recorder/src/parsers/mikan/mikan_ep_parser.rs deleted file mode 100644 index cc955af..0000000 --- a/crates/recorder/src/parsers/mikan/mikan_ep_parser.rs +++ /dev/null @@ -1,127 +0,0 @@ -use bytes::Bytes; -use html_escape::decode_html_entities; -use lazy_static::lazy_static; -use lightningcss::{properties::Property, values::image::Image}; -use regex::Regex; -use url::Url; - -use crate::{ - downloaders::{html::download_html, image::download_image}, - parsers::html::{get_tag_style, query_selector_first_tag}, -}; - -pub struct MikanEpisodeMeta { - pub homepage: Url, - pub poster_data: Option, - pub origin_poster_src: Option, - pub official_title: String, -} - -lazy_static! { - static ref MIKAN_TITLE_SEASON: Regex = Regex::new("第.*季").unwrap(); -} - -pub async fn parse_episode_meta_from_mikan_homepage( - url: Url, -) -> eyre::Result> { - let url_host = url.origin().unicode_serialization(); - let content = download_html(url.as_str()).await?; - let dom = tl::parse(&content, tl::ParserOptions::default())?; - let parser = dom.parser(); - let poster_node = query_selector_first_tag(&dom, r"div.bangumi-poster", parser); - let official_title_node = query_selector_first_tag(&dom, r"p.bangumi-title", parser); - let mut origin_poster_src = None; - if let Some(style) = poster_node.and_then(get_tag_style) { - for (prop, _) in style.iter() { - match prop { - Property::BackgroundImage(images) => { - if let Some(Image::Url(path)) = images.first() { - if let Ok(url) = Url::parse(&url_host).and_then(|s| s.join(path.url.trim())) - { - origin_poster_src = Some(url); - } - } - } - Property::Background(backgrounds) => { - for bg in backgrounds { - if let Image::Url(path) = &bg.image { - if let Ok(url) = - Url::parse(&url_host).and_then(|s| s.join(path.url.trim())) - { - origin_poster_src = Some(url); - } - } - } - } - _ => {} - } - } - }; - origin_poster_src = origin_poster_src.map(|mut p| { - p.set_query(None); - p - }); - let poster_data = if let Some(p) = origin_poster_src.as_ref() { - download_image(p.clone()).await.ok() - } else { - None - }; - let meta = official_title_node - .map(|s| s.inner_text(parser)) - .and_then(|official_title| { - let title = MIKAN_TITLE_SEASON - .replace(&decode_html_entities(&official_title), "") - .trim() - .to_string(); - if title.is_empty() { - None - } else { - Some(title) - } - }) - .map(|title| MikanEpisodeMeta { - homepage: url, - poster_data, - official_title: title, - origin_poster_src, - }); - Ok(meta) -} - -#[cfg(test)] -mod test { - use url::Url; - - use super::parse_episode_meta_from_mikan_homepage; - - #[tokio::test] - async fn test_parse_mikan() { - let test_fn = async || -> eyre::Result<()> { - let url_str = - "https://mikanani.me/Home/Episode/475184dce83ea2b82902592a5ac3343f6d54b36a"; - let url = Url::parse(url_str)?; - - if let Some(ep_meta) = parse_episode_meta_from_mikan_homepage(url.clone()).await? { - assert_eq!(ep_meta.homepage, url); - assert_eq!(ep_meta.official_title, "葬送的芙莉莲"); - assert_eq!( - ep_meta.origin_poster_src, - Some(Url::parse( - "https://mikanani.me/images/Bangumi/202309/5ce9fed1.jpg" - )?) - ); - let u8_data = ep_meta.poster_data.expect("should have poster data"); - assert!( - u8_data.starts_with(&[255, 216, 255, 224]), - "should start with valid jpeg data magic number" - ); - } else { - panic!("can not find mikan episode title") - } - - Ok(()) - }; - - test_fn().await.expect("test parse mikan failed"); - } -} diff --git a/crates/recorder/src/parsers/mikan/mikan_rss_parser.rs b/crates/recorder/src/parsers/mikan/mikan_rss_parser.rs deleted file mode 100644 index 5175809..0000000 --- a/crates/recorder/src/parsers/mikan/mikan_rss_parser.rs +++ /dev/null @@ -1,88 +0,0 @@ -use chrono::DateTime; -use reqwest::IntoUrl; -use serde::{Deserialize, Serialize}; - -use crate::{ - downloaders::{bytes::download_bytes, defs::BITTORRENT_MIME_TYPE}, - parsers::errors::ParseError, -}; - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct MikanRssItem { - pub title: String, - pub homepage: Option, - pub url: String, - pub content_length: Option, - pub mime: String, - pub pub_date: Option, -} - -impl TryFrom for MikanRssItem { - type Error = ParseError; - - fn try_from(item: rss::Item) -> Result { - let mime_type = item - .enclosure() - .map(|x| x.mime_type.to_string()) - .unwrap_or_default(); - if mime_type == BITTORRENT_MIME_TYPE { - let enclosure = item.enclosure.unwrap(); - - Ok(MikanRssItem { - title: item.title.unwrap_or_default(), - homepage: item.link, - url: enclosure.url, - content_length: enclosure.length.parse().ok(), - mime: enclosure.mime_type, - pub_date: item - .pub_date - .and_then(|s| DateTime::parse_from_rfc2822(&s).ok()) - .map(|s| s.timestamp_millis()), - }) - } else { - Err(ParseError::MimeError { - expected: String::from(BITTORRENT_MIME_TYPE), - found: mime_type, - desc: String::from("MikanRssItem"), - }) - } - } -} - -pub async fn parse_mikan_rss_items_from_rss_link( - url: impl IntoUrl, -) -> eyre::Result> { - let bytes = download_bytes(url).await?; - - let channel = rss::Channel::read_from(&bytes[..])?; - - Ok(channel.items.into_iter().flat_map(MikanRssItem::try_from)) -} - -#[cfg(test)] -mod tests { - use super::parse_mikan_rss_items_from_rss_link; - use crate::downloaders::defs::BITTORRENT_MIME_TYPE; - - #[tokio::test] - pub async fn test_mikan_subscription_items_from_rss_url() { - let url = "https://mikanani.me/RSS/Bangumi?bangumiId=3141&subgroupid=370"; - let items = parse_mikan_rss_items_from_rss_link(url) - .await - .expect("should get subscription items from rss url") - .collect::>(); - - let first_sub_item = items - .first() - .expect("mikan subscriptions should have at least one subs"); - - assert_eq!(first_sub_item.mime, BITTORRENT_MIME_TYPE); - let homepage = first_sub_item - .homepage - .as_ref() - .expect("mikan subscription item should have home page"); - assert!(homepage.starts_with("https://mikanani.me/Home/Episode")); - let name = first_sub_item.title.as_str(); - assert!(name.contains("葬送的芙莉莲")); - } -} diff --git a/crates/recorder/src/parsers/mikan/mod.rs b/crates/recorder/src/parsers/mikan/mod.rs deleted file mode 100644 index 831a0e8..0000000 --- a/crates/recorder/src/parsers/mikan/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub mod mikan_ep_parser; -pub mod mikan_rss_parser; - -pub use mikan_ep_parser::{parse_episode_meta_from_mikan_homepage, MikanEpisodeMeta}; -pub use mikan_rss_parser::{parse_mikan_rss_items_from_rss_link, MikanRssItem}; diff --git a/crates/recorder/src/parsers/raw/mod.rs b/crates/recorder/src/parsers/raw/mod.rs deleted file mode 100644 index d74c17f..0000000 --- a/crates/recorder/src/parsers/raw/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod raw_ep_parser; - -pub use raw_ep_parser::{parse_episode_meta_from_raw_name, RawEpisodeMeta}; diff --git a/crates/recorder/src/parsers/title_parser.rs b/crates/recorder/src/parsers/title_parser.rs deleted file mode 100644 index 8b13789..0000000 --- a/crates/recorder/src/parsers/title_parser.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/crates/recorder/src/parsers/torrent/mod.rs b/crates/recorder/src/parsers/torrent/mod.rs deleted file mode 100644 index fd227b3..0000000 --- a/crates/recorder/src/parsers/torrent/mod.rs +++ /dev/null @@ -1 +0,0 @@ -mod torrent_ep_parser; diff --git a/crates/recorder/src/path/mod.rs b/crates/recorder/src/path/mod.rs deleted file mode 100644 index f8de902..0000000 --- a/crates/recorder/src/path/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod torrent_path; diff --git a/crates/recorder/src/path/torrent_path.rs b/crates/recorder/src/path/torrent_path.rs deleted file mode 100644 index c4bf58c..0000000 --- a/crates/recorder/src/path/torrent_path.rs +++ /dev/null @@ -1,79 +0,0 @@ -use std::collections::HashSet; - -use quirks_path::{Path, PathBuf}; - -use crate::{ - downloaders::defs::Torrent, - models::{bangumi, subscribers}, - parsers::defs::SEASON_REGEX, -}; - -pub fn check_files(info: &Torrent) -> (Vec, Vec) { - let mut media_list = vec![]; - let mut subtitle_list = vec![]; - for f in info.iter_files() { - let file_name = PathBuf::from(f.get_name()); - let extension = file_name.extension().unwrap_or_default().to_lowercase(); - - match extension.as_str() { - ".mp4" | ".mkv" => { - media_list.push(file_name); - } - ".ass" | ".srt" => subtitle_list.push(file_name), - _ => {} - } - } - - (media_list, subtitle_list) -} - -pub fn path_to_bangumi<'a>( - save_path: &'a Path, - downloader_path: &'a Path, -) -> Option<(&'a str, i32)> { - let downloader_parts = downloader_path - .components() - .map(|s| s.as_str()) - .collect::>(); - - let mut season = None; - let mut bangumi_name = None; - for part in save_path.components().map(|s| s.as_str()) { - if let Some(match_result) = SEASON_REGEX.captures(part) { - season = Some( - match_result - .get(2) - .unwrap_or_else(|| unreachable!("must have a season")) - .as_str() - .parse::() - .unwrap_or_else(|e| unreachable!("{}", e.to_string())), - ); - } else if !downloader_parts.contains(part) { - bangumi_name = Some(part); - } - } - match (season, bangumi_name) { - (Some(season), Some(bangumi_name)) => Some((bangumi_name, season)), - _ => None, - } -} - -pub fn file_depth(path: &Path) -> usize { - path.components().count() -} - -pub fn is_ep(path: &Path) -> bool { - file_depth(path) <= 2 -} - -pub fn gen_bangumi_sub_path(data: &bangumi::Model) -> PathBuf { - PathBuf::from(data.official_title.to_string()).join(format!("Season {}", data.season)) -} - -pub fn rule_name(bgm: &bangumi::Model, conf: &subscribers::SubscriberBangumiConfig) -> String { - if let (Some(true), Some(group_name)) = (conf.leading_group_tag, &bgm.fansub) { - format!("[{}] {} S{}", group_name, bgm.official_title, bgm.season) - } else { - format!("{} S{}", bgm.official_title, bgm.season) - } -} diff --git a/crates/recorder/src/workers/subscription_worker.rs b/crates/recorder/src/workers/subscription_worker.rs index 7c4b5a9..f806c3f 100644 --- a/crates/recorder/src/workers/subscription_worker.rs +++ b/crates/recorder/src/workers/subscription_worker.rs @@ -12,19 +12,18 @@ pub struct SubscriptionWorkerArgs { pub subscription: subscriptions::Model, } -impl worker::AppWorker for SubscriptionWorker { +#[async_trait] + +impl BackgroundWorker for SubscriptionWorker { fn build(ctx: &AppContext) -> Self { Self { ctx: ctx.clone() } } -} -#[async_trait] -impl worker::Worker for SubscriptionWorker { - async fn perform(&self, args: SubscriptionWorkerArgs) -> worker::Result<()> { + async fn perform(&self, _args: SubscriptionWorkerArgs) -> Result<()> { println!("================================================"); - let db = &self.ctx.db; - let storage = &self.ctx.storage; + let _db = &self.ctx.db; + let _storage = &self.ctx.storage; println!("================================================"); Ok(()) diff --git a/crates/torrent/.gitignore b/crates/torrent/.gitignore new file mode 100644 index 0000000..8861634 --- /dev/null +++ b/crates/torrent/.gitignore @@ -0,0 +1,17 @@ +**/config/local.yaml +**/config/*.local.yaml + +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb diff --git a/crates/torrent/Cargo.toml b/crates/torrent/Cargo.toml new file mode 100644 index 0000000..8eaf0d2 --- /dev/null +++ b/crates/torrent/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "torrent" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[lib] +name = "torrent" +path = "src/lib.rs" + +[features] +default = [] +testcontainers = [] + +[dependencies] +async-trait = "0.1.83" +chrono = "0.4.39" +eyre = "0.6.12" +futures = "0.3.31" +itertools = "0.13.0" +lazy_static = "1.5.0" +librqbit-core = "4" +qbit-rs = { git = "https://github.com/lonelyhentxi/qbit.git", rev = "a2c70aa", features = [ + "default", + "builder", +] } +regex = "1.11.1" +serde = "1.0.216" +thiserror = "2.0.9" +tokio = "1.42.0" +url = "2.5.4" +quirks_path = { path = "../quirks-path" } +reqwest = "0.12.11" +bytes = "1.9.0" + +[dev-dependencies] +testcontainers = { version = "0.23.1" } +testcontainers-modules = { version = "0.11.4" } diff --git a/crates/recorder/src/downloaders/defs.rs b/crates/torrent/src/core.rs similarity index 68% rename from crates/recorder/src/downloaders/defs.rs rename to crates/torrent/src/core.rs index bb0cd8f..215af24 100644 --- a/crates/recorder/src/downloaders/defs.rs +++ b/crates/torrent/src/core.rs @@ -1,22 +1,29 @@ +use bytes::Bytes; use itertools::Itertools; use lazy_static::lazy_static; use librqbit_core::{ magnet::Magnet, torrent_metainfo::{torrent_from_bytes, TorrentMetaV1Owned}, }; -pub use qbit_rs::model::{ - Torrent as QbitTorrent, TorrentContent as QbitTorrentContent, - TorrentFilter as QbitTorrentFilter, TorrentSource as QbitTorrentSource, -}; +use quirks_path::{Path, PathBuf}; use regex::Regex; +use reqwest::IntoUrl; use serde::{Deserialize, Serialize}; use url::Url; -use crate::downloaders::{bytes::download_bytes, error::DownloaderError}; +use crate::{QbitTorrent, QbitTorrentContent, TorrentDownloadError}; pub const BITTORRENT_MIME_TYPE: &str = "application/x-bittorrent"; pub const MAGNET_SCHEMA: &str = "magnet"; -pub const DEFAULT_USER_AGENT: &str = "Wget/1.13.4 (linux-gnu)"; +pub const DEFAULT_TORRENT_USER_AGENT: &str = "Wget/1.13.4 (linux-gnu)"; + +async fn download_torrent_file(url: T) -> eyre::Result { + let request_client = reqwest::Client::builder() + .user_agent(DEFAULT_TORRENT_USER_AGENT) + .build()?; + let bytes = request_client.get(url).send().await?.bytes().await?; + Ok(bytes) +} #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] @@ -34,24 +41,6 @@ pub enum TorrentFilter { Errored, } -impl From for QbitTorrentFilter { - fn from(val: TorrentFilter) -> Self { - match val { - TorrentFilter::All => QbitTorrentFilter::All, - TorrentFilter::Downloading => QbitTorrentFilter::Downloading, - TorrentFilter::Completed => QbitTorrentFilter::Completed, - TorrentFilter::Paused => QbitTorrentFilter::Paused, - TorrentFilter::Active => QbitTorrentFilter::Active, - TorrentFilter::Inactive => QbitTorrentFilter::Inactive, - TorrentFilter::Resumed => QbitTorrentFilter::Resumed, - TorrentFilter::Stalled => QbitTorrentFilter::Stalled, - TorrentFilter::StalledUploading => QbitTorrentFilter::StalledUploading, - TorrentFilter::StalledDownloading => QbitTorrentFilter::StalledDownloading, - TorrentFilter::Errored => QbitTorrentFilter::Errored, - } - } -} - lazy_static! { static ref TORRENT_HASH_RE: Regex = Regex::new(r"[a-fA-F0-9]{40}").unwrap(); static ref TORRENT_EXT_RE: Regex = Regex::new(r"\.torrent$").unwrap(); @@ -90,19 +79,19 @@ impl TorrentSource { ) { TorrentSource::from_torrent_url(url, match_hash.as_str().to_string())? } else { - let contents = download_bytes(url).await?; + let contents = download_torrent_file(url).await?; TorrentSource::from_torrent_file(contents.to_vec(), Some(basename.to_string()))? } } else { - let contents = download_bytes(url).await?; + let contents = download_torrent_file(url).await?; TorrentSource::from_torrent_file(contents.to_vec(), None)? }; Ok(source) } pub fn from_torrent_file(file: Vec, name: Option) -> eyre::Result { - let torrent: TorrentMetaV1Owned = - torrent_from_bytes(&file).map_err(|_| DownloaderError::InvalidTorrentFileFormat)?; + let torrent: TorrentMetaV1Owned = torrent_from_bytes(&file) + .map_err(|_| TorrentDownloadError::InvalidTorrentFileFormat)?; let hash = torrent.info_hash.as_string(); Ok(TorrentSource::TorrentFile { torrent: file, @@ -113,17 +102,24 @@ impl TorrentSource { pub fn from_magnet_url(url: Url) -> eyre::Result { if url.scheme() != MAGNET_SCHEMA { - Err(DownloaderError::InvalidUrlSchema { + Err(TorrentDownloadError::InvalidUrlSchema { found: url.scheme().to_string(), expected: MAGNET_SCHEMA.to_string(), } .into()) } else { - let magnet = - Magnet::parse(url.as_str()).map_err(|_| DownloaderError::InvalidMagnetFormat { + let magnet = Magnet::parse(url.as_str()).map_err(|_| { + TorrentDownloadError::InvalidMagnetFormat { url: url.as_str().to_string(), - })?; - let hash = magnet.info_hash.as_string(); + } + })?; + + let hash = magnet + .as_id20() + .ok_or_else(|| TorrentDownloadError::InvalidMagnetFormat { + url: url.as_str().to_string(), + })? + .as_string(); Ok(TorrentSource::MagnetUrl { url, hash }) } } @@ -141,22 +137,6 @@ impl TorrentSource { } } -impl From for QbitTorrentSource { - fn from(value: TorrentSource) -> Self { - match value { - TorrentSource::MagnetUrl { url, .. } => QbitTorrentSource::Urls { - urls: qbit_rs::model::Sep::from([url]), - }, - TorrentSource::TorrentUrl { url, .. } => QbitTorrentSource::Urls { - urls: qbit_rs::model::Sep::from([url]), - }, - TorrentSource::TorrentFile { - torrent: torrents, .. - } => QbitTorrentSource::TorrentFiles { torrents }, - } - } -} - pub trait TorrentContent { fn get_name(&self) -> &str; @@ -247,3 +227,43 @@ impl Torrent { } } } + +#[async_trait::async_trait] +pub trait TorrentDownloader { + async fn get_torrents_info( + &self, + status_filter: TorrentFilter, + category: Option, + tag: Option, + ) -> eyre::Result>; + + async fn add_torrents( + &self, + source: TorrentSource, + save_path: String, + category: Option<&str>, + ) -> eyre::Result<()>; + + async fn delete_torrents(&self, hashes: Vec) -> eyre::Result<()>; + + async fn rename_torrent_file( + &self, + hash: &str, + old_path: &str, + new_path: &str, + ) -> eyre::Result<()>; + + async fn move_torrents(&self, hashes: Vec, new_path: &str) -> eyre::Result<()>; + + async fn get_torrent_path(&self, hashes: String) -> eyre::Result>; + + async fn check_connection(&self) -> eyre::Result<()>; + + async fn set_torrents_category(&self, hashes: Vec, category: &str) -> eyre::Result<()>; + + async fn add_torrent_tags(&self, hashes: Vec, tags: Vec) -> eyre::Result<()>; + + async fn add_category(&self, category: &str) -> eyre::Result<()>; + + fn get_save_path(&self, sub_path: &Path) -> PathBuf; +} diff --git a/crates/recorder/src/downloaders/error.rs b/crates/torrent/src/error.rs similarity index 92% rename from crates/recorder/src/downloaders/error.rs rename to crates/torrent/src/error.rs index b70299d..6c48d16 100644 --- a/crates/recorder/src/downloaders/error.rs +++ b/crates/torrent/src/error.rs @@ -1,9 +1,9 @@ -use std::borrow::Cow; +use std::{borrow::Cow, time::Duration}; + use thiserror::Error; -use std::time::Duration; #[derive(Error, Debug)] -pub enum DownloaderError { +pub enum TorrentDownloadError { #[error("Invalid mime (expected {expected:?}, got {found:?})")] InvalidMime { expected: String, found: String }, #[error("Invalid url schema (expected {expected:?}, got {found:?})")] diff --git a/crates/torrent/src/lib.rs b/crates/torrent/src/lib.rs new file mode 100644 index 0000000..e324d1a --- /dev/null +++ b/crates/torrent/src/lib.rs @@ -0,0 +1,11 @@ +pub mod core; +pub mod error; +pub mod qbit; + +pub use core::{Torrent, TorrentContent, TorrentDownloader, TorrentFilter, TorrentSource}; + +pub use error::TorrentDownloadError; +pub use qbit::{ + QBittorrentDownloader, QBittorrentDownloaderCreation, QbitTorrent, QbitTorrentContent, + QbitTorrentFile, QbitTorrentFilter, QbitTorrentSource, +}; diff --git a/crates/recorder/src/downloaders/qbitorrent.rs b/crates/torrent/src/qbit.rs similarity index 84% rename from crates/recorder/src/downloaders/qbitorrent.rs rename to crates/torrent/src/qbit.rs index 4bcc966..cec26ea 100644 --- a/crates/recorder/src/downloaders/qbitorrent.rs +++ b/crates/torrent/src/qbit.rs @@ -1,14 +1,13 @@ use std::{ - borrow::Cow, - collections::{HashMap, HashSet}, - fmt::Debug, - future::Future, - sync::Arc, - time::Duration, + borrow::Cow, collections::HashSet, fmt::Debug, future::Future, sync::Arc, time::Duration, }; use eyre::OptionExt; use futures::future::try_join_all; +pub use qbit_rs::model::{ + Torrent as QbitTorrent, TorrentContent as QbitTorrentContent, TorrentFile as QbitTorrentFile, + TorrentFilter as QbitTorrentFilter, TorrentSource as QbitTorrentSource, +}; use qbit_rs::{ model::{AddTorrentArg, Credential, GetTorrentListArg, NonEmptyStr, SyncData}, Qbit, @@ -17,18 +16,55 @@ use quirks_path::{path_equals_as_file_url, Path, PathBuf}; use tokio::time::sleep; use url::Url; -use super::{ - defs::{Torrent, TorrentFilter, TorrentSource}, - error::DownloaderError, - torrent_downloader::TorrentDownloader, -}; -use crate::{ - downloaders::defs::{QbitTorrent, QbitTorrentContent, TorrentContent}, - models::{entities::downloaders, prelude::DownloaderCategory}, -}; +use crate::{Torrent, TorrentDownloadError, TorrentDownloader, TorrentFilter, TorrentSource}; -pub struct SyncDataCache { - pub torrents: HashMap, +impl From for QbitTorrentSource { + fn from(value: TorrentSource) -> Self { + match value { + TorrentSource::MagnetUrl { url, .. } => QbitTorrentSource::Urls { + urls: qbit_rs::model::Sep::from([url]), + }, + TorrentSource::TorrentUrl { url, .. } => QbitTorrentSource::Urls { + urls: qbit_rs::model::Sep::from([url]), + }, + TorrentSource::TorrentFile { + torrent: torrents, + name, + .. + } => QbitTorrentSource::TorrentFiles { + torrents: vec![QbitTorrentFile { + filename: name.unwrap_or_default(), + data: torrents, + }], + }, + } + } +} + +impl From for QbitTorrentFilter { + fn from(val: TorrentFilter) -> Self { + match val { + TorrentFilter::All => QbitTorrentFilter::All, + TorrentFilter::Downloading => QbitTorrentFilter::Downloading, + TorrentFilter::Completed => QbitTorrentFilter::Completed, + TorrentFilter::Paused => QbitTorrentFilter::Paused, + TorrentFilter::Active => QbitTorrentFilter::Active, + TorrentFilter::Inactive => QbitTorrentFilter::Inactive, + TorrentFilter::Resumed => QbitTorrentFilter::Resumed, + TorrentFilter::Stalled => QbitTorrentFilter::Stalled, + TorrentFilter::StalledUploading => QbitTorrentFilter::StalledUploading, + TorrentFilter::StalledDownloading => QbitTorrentFilter::StalledDownloading, + TorrentFilter::Errored => QbitTorrentFilter::Errored, + } + } +} + +pub struct QBittorrentDownloaderCreation { + pub endpoint: String, + pub username: String, + pub password: String, + pub save_path: String, + pub subscriber_id: i32, } pub struct QBittorrentDownloader { @@ -40,32 +76,26 @@ pub struct QBittorrentDownloader { } impl QBittorrentDownloader { - pub async fn from_downloader_model(model: downloaders::Model) -> Result { - if model.category != DownloaderCategory::QBittorrent { - return Err(DownloaderError::InvalidMime { - expected: DownloaderCategory::QBittorrent.to_string(), - found: model.category.to_string(), - }); - } - - let endpoint_url = model - .endpoint_url() - .map_err(DownloaderError::InvalidUrlParse)?; - let credential = Credential::new(model.username, model.password); + pub async fn from_creation( + creation: QBittorrentDownloaderCreation, + ) -> Result { + let endpoint_url = + Url::parse(&creation.endpoint).map_err(TorrentDownloadError::InvalidUrlParse)?; + let credential = Credential::new(creation.username, creation.password); let client = Qbit::new(endpoint_url.clone(), credential); client .login(false) .await - .map_err(DownloaderError::QBitAPIError)?; + .map_err(TorrentDownloadError::QBitAPIError)?; client.sync(None).await?.rid; Ok(Self { client: Arc::new(client), endpoint_url, - subscriber_id: model.subscriber_id, - save_path: model.save_path.into(), + subscriber_id: creation.subscriber_id, + save_path: creation.save_path.into(), wait_sync_timeout: Duration::from_millis(10000), }) } @@ -102,7 +132,7 @@ impl QBittorrentDownloader { if stop_wait_fn(sync_data) { break; } else { - return Err(DownloaderError::TimeoutError { + return Err(TorrentDownloadError::TimeoutError { action: Cow::Borrowed("QBittorrentDownloader::wait_unit"), timeout, } @@ -274,8 +304,7 @@ impl TorrentDownloader for QBittorrentDownloader { hash, |contents| -> bool { contents.iter().any(|c| { - path_equals_as_file_url(save_path.join(c.get_name()), &new_path) - .unwrap_or(false) + path_equals_as_file_url(save_path.join(&c.name), &new_path).unwrap_or(false) }) }, None, @@ -467,11 +496,7 @@ pub mod tests { async fn test_qbittorrent_downloader_impl() { let base_save_path = Path::new(get_tmp_qbit_test_folder()); - let downloader = QBittorrentDownloader::from_downloader_model(downloaders::Model { - created_at: Default::default(), - updated_at: Default::default(), - id: 0, - category: DownloaderCategory::QBittorrent, + let downloader = QBittorrentDownloader::from_creation(QBittorrentDownloaderCreation { endpoint: "http://localhost:8080".to_string(), password: "".to_string(), username: "".to_string(), @@ -489,8 +514,8 @@ pub mod tests { .unwrap(); let torrent_source = TorrentSource::parse( - "https://mikanani.me/Download/20240301/47ee2d69e7f19af783ad896541a07b012676f858.torrent" - ).await.unwrap(); + "https://mikanani.me/Download/20240301/47ee2d69e7f19af783ad896541a07b012676f858.torrent" + ).await.unwrap(); let save_path = base_save_path.join(format!( "test_add_torrents_{}", diff --git a/justfile b/justfile index 71ae33e..990d0e1 100644 --- a/justfile +++ b/justfile @@ -4,10 +4,14 @@ set dotenv-load prepare-dev-recorder: cargo install loco-cli cargo install sea-orm-cli + cargo install cargo-watch dev-recorder: cargo watch -w crates/recorder -w config -x 'recorder start' +down-recorder: + cargo run -p recorder --bin recorder_cli -- db down 999 --environment recorder.development + play-recorder: cargo recorder-playground