From 2f90d71911597b70a1129d0968cad873985f4e18 Mon Sep 17 00:00:00 2001 From: Domipoke <42298508+Domipoke@users.noreply.github.com> Date: Wed, 1 Apr 2026 00:35:07 +0200 Subject: [PATCH] 01/04/26_00.35 --- .cargo/config.toml | 5 + .gitignore | 1 + Cargo.lock | 3823 ++++++++++++++++++++++++++++++ Cargo.toml | 17 + build.sh | 3 + config.toml | 12 + debug.log | 0 extensions/anime/animeworld.toml | 10 + extensions/manga/mangaworld.toml | 10 + library/animelibrary.toml | 3 + library/mangalibrary.toml | 13 + src/components.rs | 536 +++++ src/config.md | 83 + src/extension.rs | 69 + src/folders.rs | 67 + src/keybinds.md | 116 + src/library.rs | 38 + src/main.md | 233 ++ src/main.rs | 587 +++++ src/mangatab.md | 29 + src/mmain.md | 73 + src/source.rs | 146 ++ src/source/animeworld.rs | 139 ++ src/source/mangaworld.rs | 179 ++ src/utils.rs | 39 + 25 files changed, 6231 insertions(+) create mode 100644 .cargo/config.toml create mode 100755 .gitignore create mode 100644 Cargo.lock create mode 100755 Cargo.toml create mode 100644 build.sh create mode 100644 config.toml create mode 100644 debug.log create mode 100644 extensions/anime/animeworld.toml create mode 100644 extensions/manga/mangaworld.toml create mode 100644 library/animelibrary.toml create mode 100644 library/mangalibrary.toml create mode 100644 src/components.rs create mode 100644 src/config.md create mode 100644 src/extension.rs create mode 100644 src/folders.rs create mode 100644 src/keybinds.md create mode 100644 src/library.rs create mode 100755 src/main.md create mode 100644 src/main.rs create mode 100644 src/mangatab.md create mode 100644 src/mmain.md create mode 100644 src/source.rs create mode 100644 src/source/animeworld.rs create mode 100644 src/source/mangaworld.rs create mode 100644 src/utils.rs diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..e78a7bf --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,5 @@ + +[target.aarch64-linux-android] +linker = "/home/domenico/Android/Sdk/ndk/30.0.14904198/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android36-clang" +[env] +CC_aarch64_linux_android = "/home/domenico/Android/Sdk/ndk/30.0.14904198/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android36-clang" diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..270b334 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,3823 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "Aniyomi" +version = "0.1.0" +dependencies = [ + "async-trait", + "color-eyre", + "curl", + "openssl", + "ratatui", + "reqwest", + "scraper", + "serde", + "strum 0.28.0", + "tokio", + "toml", +] + +[[package]] +name = "addr2line" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "atomic" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89cbf775b137e9b968e67227ef7f775587cde3fd31b0d8599dbd0f598a48340" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "aws-lc-rs" +version = "1.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94bffc006df10ac2a68c83692d734a465f8ee6c5b384d8545a636f81d858f4bf" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4321e568ed89bb5a7d291a7f37997c2c0df89809d7b6d12062c81ddb54aa782e" +dependencies = [ + "cc", + "cmake", + "dunce", + "fs_extra", +] + +[[package]] +name = "backtrace" +version = "0.3.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-link", +] + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" + +[[package]] +name = "bytemuck" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + +[[package]] +name = "castaway" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a" +dependencies = [ + "rustversion", +] + +[[package]] +name = "cc" +version = "1.2.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "cmake" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d" +dependencies = [ + "cc", +] + +[[package]] +name = "color-eyre" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5920befb47832a6d61ee3a3a846565cfa39b331331e68a3b1d1116630f2f26d" +dependencies = [ + "backtrace", + "color-spantrace", + "eyre", + "indenter", + "once_cell", + "owo-colors", + "tracing-error", +] + +[[package]] +name = "color-spantrace" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8b88ea9df13354b55bc7234ebcce36e6ef896aca2e42a15de9e10edce01b427" +dependencies = [ + "once_cell", + "owo-colors", + "tracing-core", + "tracing-error", +] + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "compact_str" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb1325a1cece981e8a296ab8f0f9b63ae357bd0784a9faaf548cc7b480707a" +dependencies = [ + "castaway", + "cfg-if", + "itoa", + "rustversion", + "ryu", + "static_assertions", +] + +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crossterm" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" +dependencies = [ + "bitflags 2.11.0", + "crossterm_winapi", + "derive_more", + "document-features", + "mio", + "parking_lot", + "rustix", + "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", +] + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "csscolorparser" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb2a7d3066da2de787b7f032c736763eb7ae5d355f81a68bab2675a96008b0bf" +dependencies = [ + "lab", + "phf 0.11.3", +] + +[[package]] +name = "cssparser" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dae61cf9c0abb83bd659dab65b7e4e38d8236824c85f0f804f173567bda257d2" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa", + "phf 0.13.1", + "smallvec", +] + +[[package]] +name = "cssparser-macros" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" +dependencies = [ + "quote", + "syn 2.0.117", +] + +[[package]] +name = "curl" +version = "0.4.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79fc3b6dd0b87ba36e565715bf9a2ced221311db47bd18011676f24a6066edbc" +dependencies = [ + "curl-sys", + "libc", + "openssl-probe 0.1.6", + "openssl-sys", + "schannel", + "socket2", + "windows-sys 0.59.0", +] + +[[package]] +name = "curl-sys" +version = "0.4.85+curl-8.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0efa6142b5ecc05f6d3eaa39e6af4888b9d3939273fb592c92b7088a8cf3fdb" +dependencies = [ + "cc", + "libc", + "libz-sys", + "openssl-sys", + "pkg-config", + "vcpkg", + "windows-sys 0.59.0", +] + +[[package]] +name = "darling" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" +dependencies = [ + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.117", +] + +[[package]] +name = "darling_macro" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "deltae" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5729f5117e208430e437df2f4843f5e5952997175992d1414f94c57d61e270b4" + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "derive_more" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.117", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] + +[[package]] +name = "dtoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c3cf4824e2d5f025c7b531afcb2325364084a16806f6d47fbc1f5fbd9960590" + +[[package]] +name = "dtoa-short" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87" +dependencies = [ + "dtoa", +] + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[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.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "euclid" +version = "0.22.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df61bf483e837f88d5c2291dcf55c67be7e676b3a51acc48db3a7b163b91ed63" +dependencies = [ + "num-traits", +] + +[[package]] +name = "eyre" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" +dependencies = [ + "indenter", + "once_cell", +] + +[[package]] +name = "fancy-regex" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b95f7c0680e4142284cf8b22c14a476e87d61b004a3a0861872b32ef7ead40a2" +dependencies = [ + "bit-set", + "regex", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "filedescriptor" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e40758ed24c9b2eeb76c35fb0aebc66c626084edd827e07e1552279814c6682d" +dependencies = [ + "libc", + "thiserror 1.0.69", + "winapi", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "finl_unicode" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9844ddc3a6e533d62bba727eb6c28b5d360921d5175e9ff0f1e621a5c590a4d5" + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + +[[package]] +name = "futf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" +dependencies = [ + "mac", + "new_debug_unreachable", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getopts" +version = "0.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe4fbac503b8d1f88e6676011885f34b7174f46e59956bba534ba83abded4df" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi 5.3.0", + "wasip2", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", + "wasip2", + "wasip3", +] + +[[package]] +name = "gimli" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" + +[[package]] +name = "h2" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash 0.1.5", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash 0.2.0", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "html5ever" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6452c4751a24e1b99c3260d505eaeee76a050573e61f30ac2c924ddc7236f01e" +dependencies = [ + "log", + "markup5ever", +] + +[[package]] +name = "http" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "hyper" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "pin-utils", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "system-configuration", + "tokio", + "tower-service", + "tracing", + "windows-registry", +] + +[[package]] +name = "icu_collections" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" + +[[package]] +name = "icu_properties" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" + +[[package]] +name = "icu_provider" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indenter" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5" + +[[package]] +name = "indexmap" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +dependencies = [ + "equivalent", + "hashbrown 0.16.1", + "serde", + "serde_core", +] + +[[package]] +name = "indoc" +version = "2.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" +dependencies = [ + "rustversion", +] + +[[package]] +name = "instability" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357b7205c6cd18dd2c86ed312d1e70add149aea98e7ef72b9fdf0270e555c11d" +dependencies = [ + "darling", + "indoc", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "ipnet" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" + +[[package]] +name = "iri-string" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.4", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "kasuari" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fe90c1150662e858c7d5f945089b7517b0a80d8bf7ba4b1b5ffc984e7230a5b" +dependencies = [ + "hashbrown 0.16.1", + "portable-atomic", + "thiserror 2.0.18", +] + +[[package]] +name = "lab" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf36173d4167ed999940f804952e6b08197cae5ad5d572eb4db150ce8ad5d58f" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "libc" +version = "0.2.182" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" + +[[package]] +name = "libz-sys" +version = "1.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52f4c29e2a68ac30c9087e1b772dc9f44a2b66ed44edf2266cf2be9b03dafc1" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "line-clipping" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f4de44e98ddbf09375cbf4d17714d18f39195f4f4894e8524501726fd9a8a4a" +dependencies = [ + "bitflags 2.11.0", +] + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "litemap" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" + +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "lru" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1dc47f592c06f33f8e3aea9591776ec7c9f9e4124778ff8a3c3b87159f7e593" +dependencies = [ + "hashbrown 0.16.1", +] + +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + +[[package]] +name = "mac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" + +[[package]] +name = "mac_address" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0aeb26bf5e836cc1c341c8106051b573f1766dfa05aa87f0b98be5e51b02303" +dependencies = [ + "nix", + "winapi", +] + +[[package]] +name = "markup5ever" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c3294c4d74d0742910f8c7b466f44dda9eb2d5742c1e430138df290a1e8451c" +dependencies = [ + "log", + "tendril", + "web_atoms", +] + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "memmem" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a64a92489e2744ce060c349162be1c5f33c6969234104dbd99ddb5feb08b8c15" + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags 2.11.0", + "cfg-if", + "cfg_aliases", + "libc", + "memoffset", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-conv" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" + +[[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.117", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", +] + +[[package]] +name = "object" +version = "0.37.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "openssl" +version = "0.10.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "951c002c75e16ea2c65b8c7e4d3d51d5530d8dfa7d060b4776828c88cfb18ecf" +dependencies = [ + "bitflags 2.11.0", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + +[[package]] +name = "openssl-src" +version = "300.5.5+3.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f1787d533e03597a7934fd0a765f0d28e94ecc5fb7789f8053b1e699a56f709" +dependencies = [ + "cc", +] + +[[package]] +name = "openssl-sys" +version = "0.9.112" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d55af3b3e226502be1526dfdba67ab0e9c96fc293004e79576b2b9edb0dbdb" +dependencies = [ + "cc", + "libc", + "openssl-src", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "ordered-float" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bb71e1b3fa6ca1c61f383464aaf2bb0e2f8e772a1f01d486832464de363b951" +dependencies = [ + "num-traits", +] + +[[package]] +name = "owo-colors" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d211803b9b6b570f68772237e415a029d5a50c65d382910b879fb19d3271f94d" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pest" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0848c601009d37dfa3430c4666e147e49cdcf1b92ecd3e63657d8a5f19da662" +dependencies = [ + "memchr", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11f486f1ea21e6c10ed15d5a7c77165d0ee443402f0780849d1768e7d9d6fe77" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8040c4647b13b210a963c1ed407c1ff4fdfa01c31d6d2a098218702e6664f94f" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "pest_meta" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89815c69d36021a140146f26659a81d6c2afa33d216d736dd4be5381a7362220" +dependencies = [ + "pest", + "sha2", +] + +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_macros 0.11.3", + "phf_shared 0.11.3", +] + +[[package]] +name = "phf" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" +dependencies = [ + "phf_macros 0.13.1", + "phf_shared 0.13.1", + "serde", +] + +[[package]] +name = "phf_codegen" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" +dependencies = [ + "phf_generator 0.11.3", + "phf_shared 0.11.3", +] + +[[package]] +name = "phf_codegen" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49aa7f9d80421bca176ca8dbfebe668cc7a2684708594ec9f3c0db0805d5d6e1" +dependencies = [ + "phf_generator 0.13.1", + "phf_shared 0.13.1", +] + +[[package]] +name = "phf_generator" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +dependencies = [ + "phf_shared 0.11.3", + "rand 0.8.5", +] + +[[package]] +name = "phf_generator" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" +dependencies = [ + "fastrand", + "phf_shared 0.13.1", +] + +[[package]] +name = "phf_macros" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" +dependencies = [ + "phf_generator 0.11.3", + "phf_shared 0.11.3", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "phf_macros" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" +dependencies = [ + "phf_generator 0.13.1", + "phf_shared 0.13.1", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher", +] + +[[package]] +name = "phf_shared" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "portable-atomic" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" + +[[package]] +name = "potential_utf" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +dependencies = [ + "zerovec", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.117", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror 2.0.18", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" +dependencies = [ + "aws-lc-rs", + "bytes", + "getrandom 0.3.4", + "lru-slab", + "rand 0.9.2", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror 2.0.18", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.60.2", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "ratatui" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1ce67fb8ba4446454d1c8dbaeda0557ff5e94d39d5e5ed7f10a65eb4c8266bc" +dependencies = [ + "instability", + "ratatui-core", + "ratatui-crossterm", + "ratatui-macros", + "ratatui-termwiz", + "ratatui-widgets", +] + +[[package]] +name = "ratatui-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ef8dea09a92caaf73bff7adb70b76162e5937524058a7e5bff37869cbbec293" +dependencies = [ + "bitflags 2.11.0", + "compact_str", + "hashbrown 0.16.1", + "indoc", + "itertools", + "kasuari", + "lru", + "strum 0.27.2", + "thiserror 2.0.18", + "unicode-segmentation", + "unicode-truncate", + "unicode-width", +] + +[[package]] +name = "ratatui-crossterm" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "577c9b9f652b4c121fb25c6a391dd06406d3b092ba68827e6d2f09550edc54b3" +dependencies = [ + "cfg-if", + "crossterm", + "instability", + "ratatui-core", +] + +[[package]] +name = "ratatui-macros" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7f1342a13e83e4bb9d0b793d0ea762be633f9582048c892ae9041ef39c936f4" +dependencies = [ + "ratatui-core", + "ratatui-widgets", +] + +[[package]] +name = "ratatui-termwiz" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f76fe0bd0ed4295f0321b1676732e2454024c15a35d01904ddb315afd3d545c" +dependencies = [ + "ratatui-core", + "termwiz", +] + +[[package]] +name = "ratatui-widgets" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7dbfa023cd4e604c2553483820c5fe8aa9d71a42eea5aa77c6e7f35756612db" +dependencies = [ + "bitflags 2.11.0", + "hashbrown 0.16.1", + "indoc", + "instability", + "itertools", + "line-clipping", + "ratatui-core", + "strum 0.27.2", + "time", + "unicode-segmentation", + "unicode-width", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.11.0", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "reqwest" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "mime", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pki-types", + "rustls-platform-verifier", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags 2.11.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls" +version = "0.23.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" +dependencies = [ + "aws-lc-rs", + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" +dependencies = [ + "openssl-probe 0.2.1", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pki-types" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" +dependencies = [ + "web-time", + "zeroize", +] + +[[package]] +name = "rustls-platform-verifier" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784" +dependencies = [ + "core-foundation 0.10.1", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" + +[[package]] +name = "rustls-webpki" +version = "0.103.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" +dependencies = [ + "aws-lc-rs", + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "scraper" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93cecd86d6259499c844440546d02f55f3e17bd286e529e48d1f9f67e92315cb" +dependencies = [ + "cssparser", + "ego-tree", + "getopts", + "html5ever", + "precomputed-hash", + "selectors", + "tendril", +] + +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags 2.11.0", + "core-foundation 0.10.1", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "selectors" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "feef350c36147532e1b79ea5c1f3791373e61cbd9a6a2615413b3807bb164fb7" +dependencies = [ + "bitflags 2.11.0", + "cssparser", + "derive_more", + "log", + "new_debug_unreachable", + "phf 0.13.1", + "phf_codegen 0.13.1", + "precomputed-hash", + "rustc-hash", + "servo_arc", + "smallvec", +] + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_spanned" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" +dependencies = [ + "serde_core", +] + +[[package]] +name = "servo_arc" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "170fb83ab34de17dc69aa7c67482b22218ddb85da56546f9bd6b929e32a05930" +dependencies = [ + "stable_deref_trait", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc" +dependencies = [ + "libc", + "mio", + "signal-hook", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "siphasher" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "string_cache" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18596f8c785a729f2819c0f6a7eae6ebeebdfffbfe4214ae6b087f690e31901" +dependencies = [ + "new_debug_unreachable", + "parking_lot", + "phf_shared 0.13.1", + "precomputed-hash", +] + +[[package]] +name = "string_cache_codegen" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585635e46db231059f76c5849798146164652513eb9e8ab2685939dd90f29b69" +dependencies = [ + "phf_generator 0.13.1", + "phf_shared 0.13.1", + "proc-macro2", + "quote", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" +dependencies = [ + "strum_macros 0.27.2", +] + +[[package]] +name = "strum" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9628de9b8791db39ceda2b119bbe13134770b56c138ec1d3af810d045c04f9bd" +dependencies = [ + "strum_macros 0.28.0", +] + +[[package]] +name = "strum_macros" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "strum_macros" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab85eea0270ee17587ed4156089e10b9e6880ee688791d45a905f5b1ca36f664" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "system-configuration" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" +dependencies = [ + "bitflags 2.11.0", + "core-foundation 0.9.4", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[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 = "terminfo" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4ea810f0692f9f51b382fff5893887bb4580f5fa246fde546e0b13e7fcee662" +dependencies = [ + "fnv", + "nom", + "phf 0.11.3", + "phf_codegen 0.11.3", +] + +[[package]] +name = "termios" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "411c5bf740737c7918b8b1fe232dca4dc9f8e754b8ad5e20966814001ed0ac6b" +dependencies = [ + "libc", +] + +[[package]] +name = "termwiz" +version = "0.23.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4676b37242ccbd1aabf56edb093a4827dc49086c0ffd764a5705899e0f35f8f7" +dependencies = [ + "anyhow", + "base64", + "bitflags 2.11.0", + "fancy-regex", + "filedescriptor", + "finl_unicode", + "fixedbitset", + "hex", + "lazy_static", + "libc", + "log", + "memmem", + "nix", + "num-derive", + "num-traits", + "ordered-float", + "pest", + "pest_derive", + "phf 0.11.3", + "sha2", + "signal-hook", + "siphasher", + "terminfo", + "termios", + "thiserror 1.0.69", + "ucd-trie", + "unicode-segmentation", + "vtparse", + "wezterm-bidi", + "wezterm-blob-leases", + "wezterm-color-types", + "wezterm-dynamic", + "wezterm-input-types", + "winapi", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl 2.0.18", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "time" +version = "0.3.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +dependencies = [ + "deranged", + "libc", + "num-conv", + "num_threads", + "powerfmt", + "serde_core", + "time-core", +] + +[[package]] +name = "time-core" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" + +[[package]] +name = "tinystr" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d" +dependencies = [ + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "1.0.4+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c94c3321114413476740df133f0d8862c61d87c8d26f04c6841e033c8c80db47" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned", + "toml_datetime", + "toml_parser", + "toml_writer", + "winnow", +] + +[[package]] +name = "toml_datetime" +version = "1.0.0+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32c2555c699578a4f59f0cc68e5116c8d7cabbd45e1409b989d4be085b53f13e" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_parser" +version = "1.0.9+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4" +dependencies = [ + "winnow", +] + +[[package]] +name = "toml_writer" +version = "1.0.6+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" +dependencies = [ + "bitflags 2.11.0", + "bytes", + "futures-util", + "http", + "http-body", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-error" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b1581020d7a273442f5b45074a6a57d5757ad0a47dac0e9f0bd57b81936f3db" +dependencies = [ + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" +dependencies = [ + "sharded-slab", + "thread_local", + "tracing-core", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "unicode-truncate" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b380a1238663e5f8a691f9039c73e1cdae598a30e9855f541d29b08b53e9a5" +dependencies = [ + "itertools", + "unicode-segmentation", + "unicode-width", +] + +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "uuid" +version = "1.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b672338555252d43fd2240c714dc444b8c6fb0a5c5335e65a07bba7742735ddb" +dependencies = [ + "atomic", + "getrandom 0.4.2", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "vtparse" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d9b2acfb050df409c972a37d3b8e08cdea3bddb0c09db9d53137e504cfabed0" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9c5522b3a28661442748e09d40924dfb9ca614b21c00d3fd135720e48b67db8" +dependencies = [ + "cfg-if", + "futures-util", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 2.0.117", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags 2.11.0", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "web-sys" +version = "0.3.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9" +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", +] + +[[package]] +name = "web_atoms" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a9779e9f04d2ac1ce317aee707aa2f6b773afba7b931222bff6983843b1576" +dependencies = [ + "phf 0.13.1", + "phf_codegen 0.13.1", + "string_cache", + "string_cache_codegen", +] + +[[package]] +name = "webpki-root-certs" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "wezterm-bidi" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0a6e355560527dd2d1cf7890652f4f09bb3433b6aadade4c9b5ed76de5f3ec" +dependencies = [ + "log", + "wezterm-dynamic", +] + +[[package]] +name = "wezterm-blob-leases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692daff6d93d94e29e4114544ef6d5c942a7ed998b37abdc19b17136ea428eb7" +dependencies = [ + "getrandom 0.3.4", + "mac_address", + "sha2", + "thiserror 1.0.69", + "uuid", +] + +[[package]] +name = "wezterm-color-types" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7de81ef35c9010270d63772bebef2f2d6d1f2d20a983d27505ac850b8c4b4296" +dependencies = [ + "csscolorparser", + "deltae", + "lazy_static", + "wezterm-dynamic", +] + +[[package]] +name = "wezterm-dynamic" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f2ab60e120fd6eaa68d9567f3226e876684639d22a4219b313ff69ec0ccd5ac" +dependencies = [ + "log", + "ordered-float", + "strsim", + "thiserror 1.0.69", + "wezterm-dynamic-derive", +] + +[[package]] +name = "wezterm-dynamic-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c0cf2d539c645b448eaffec9ec494b8b19bd5077d9e58cb1ae7efece8d575b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "wezterm-input-types" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7012add459f951456ec9d6c7e6fc340b1ce15d6fc9629f8c42853412c029e57e" +dependencies = [ + "bitflags 1.3.2", + "euclid", + "lazy_static", + "serde", + "wezterm-dynamic", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" +dependencies = [ + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "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]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "winnow" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn 2.0.117", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn 2.0.117", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags 2.11.0", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "writeable" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" + +[[package]] +name = "yoke" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a789c6e490b576db9f7e6b6d661bcc9799f7c0ac8352f56ea20193b2681532e5" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f65c489a7071a749c849713807783f70672b28094011623e200cb86dcb835953" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + +[[package]] +name = "zerotrie" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/Cargo.toml b/Cargo.toml new file mode 100755 index 0000000..ec374d2 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "Aniyomi" +version = "0.1.0" +edition = "2024" + +[dependencies] +async-trait = "0.1.89" +color-eyre = "0.6.5" +curl = "0.4.49" +ratatui = "0.30.0" +reqwest = { version = "0.13.2", features = ["rustls"] } +scraper = "0.25.0" +serde = { version = "1.0.228", features = ["derive"] } +strum = { version = "0.28", default-features = false, features = ["derive"] } +tokio = { version = "1.50.0", features = ["full"] } +toml = "1.0.4" +openssl = { version = "0.10", features = ["vendored"] } \ No newline at end of file diff --git a/build.sh b/build.sh new file mode 100644 index 0000000..b6adc3a --- /dev/null +++ b/build.sh @@ -0,0 +1,3 @@ +# cargo build --target aarch64-linux-android --release +cargo build --release +cargo build --target x86_64-pc-windows-gnu --release \ No newline at end of file diff --git a/config.toml b/config.toml new file mode 100644 index 0000000..f25c901 --- /dev/null +++ b/config.toml @@ -0,0 +1,12 @@ + +[Settings] + +[Settings.Folders] +basedir = "." # !! Should be an Absolute Path +extensions = "./extensions" +anime = "./anime" +manga = "./manga" + +[Extension] +defaultanime = "animeworld" # USELESS +defaultmanga = "mangaworld" # USELESS diff --git a/debug.log b/debug.log new file mode 100644 index 0000000..e69de29 diff --git a/extensions/anime/animeworld.toml b/extensions/anime/animeworld.toml new file mode 100644 index 0000000..622e43e --- /dev/null +++ b/extensions/anime/animeworld.toml @@ -0,0 +1,10 @@ +Type = "Anime" + +[Info] +Name = "Animeworld" +Description = "" +Language = "IT/it" + +[Sitemanager] +Hostname = "animeworld.ac" +Url = "https://animeworld.ac" \ No newline at end of file diff --git a/extensions/manga/mangaworld.toml b/extensions/manga/mangaworld.toml new file mode 100644 index 0000000..edaa1b3 --- /dev/null +++ b/extensions/manga/mangaworld.toml @@ -0,0 +1,10 @@ +Type = "Manga" + +[Info] +Name = "Mangaworld" +Description = "" +Language = "IT/it" + +[Sitemanager] +Hostname = "mangaworld.mx" +Url = "https://www.mangaworld.mx" diff --git a/library/animelibrary.toml b/library/animelibrary.toml new file mode 100644 index 0000000..c4b3876 --- /dev/null +++ b/library/animelibrary.toml @@ -0,0 +1,3 @@ +[[Animeworld]] +name="Frieren: Beyond Journey's End 2" +episodes="https://www.animeworld.ac/play/frieren-beyond-journeys-end-2.JvOQx" diff --git a/library/mangalibrary.toml b/library/mangalibrary.toml new file mode 100644 index 0000000..3968506 --- /dev/null +++ b/library/mangalibrary.toml @@ -0,0 +1,13 @@ +[[Mangaworld]] +name="Kingdom" +episodes="https://www.mangaworld.mx/manga/1683/kingdom" + + +[[Mangaworld]] +name="Chainsaw Man" +episodes="https://www.mangaworld.mx/manga/1637/chainsaw-man" + + +[[Mangago]] +name="Medalist" +episodes="https://www.mangago.me/read-manga/medalist/" \ No newline at end of file diff --git a/src/components.rs b/src/components.rs new file mode 100644 index 0000000..73a974f --- /dev/null +++ b/src/components.rs @@ -0,0 +1,536 @@ +pub mod list { + use std::{collections::HashMap}; + use ratatui::{Frame, crossterm::event::KeyCode, layout::Rect, style::{Style, palette::tailwind::{self}}, widgets::{Block, BorderType, Borders, List, ListState}}; + + pub struct CurrentWidget { + pub render: Box, + pub handle_input: Box bool> + } + type SLWidgetNotOption = CurrentWidget; + type SLWidget = Option; + type Callback = fn(); + + + #[derive(Default)] + pub struct SelectableList { + children_list: Vec, + children_hash: HashMap, + path: Vec, + pub lastpath:String, + pub state: ListState, + pub widget: SLWidget + } + pub struct ListDiveChild { + pub title: String, + pub index: usize, + pub action_type: ActionType, + pub children_list: Vec, + pub children_hash: Option>, + pub widget: SLWidget, + } + impl ListDiveChild { + pub fn new(title: String, index:usize, action_type:ActionType, children: Option>, widget:SLWidget) -> Self { + let childrenlist: Vec = match children.as_ref() { + Some(chs) => { + let mut x:Vec<&ListDiveChild> = chs.values().collect(); + x.sort_by_key(|v| v.index); + x.into_iter().map(|v| v.title.to_owned()).collect() + }, + _ => vec![] + }; + Self { + title, + index, + action_type, + children_list: childrenlist, + children_hash: children, + widget + } + } + } + + #[derive(Clone)] + pub enum ActionType { + None, + SubMenu, + Render, + } + + impl SelectableList { + pub fn new( + children: HashMap,widget:SLWidget + ) -> Self { + let mut childrenlist = vec!["".to_string();children.keys().len()]; + for (_,v) in &children { + childrenlist[v.index] = v.title.to_owned(); + } + Self { + children_hash: children, + children_list: childrenlist, + path: vec![], + lastpath: "".to_owned(), + state: ListState::default(), + widget, + } + } + + + fn get_current_item_list(&self) -> &Vec { + if self.path.len() > 0 { + if let Some(first_path) = self.path.first() { + let mut ocurr = self.children_hash.get(first_path); + for i in 1..self.path.len() { + if let Some(curr) = ocurr { + if let Some(h) = &curr.children_hash { + match self.path.get(i) { + Some(p) => ocurr = h.get(p), + _ => break + } + } else { + break; + } + } else { + break; + } + } + if let Some(curr) = ocurr { + return &curr.children_list; + } + } + } + return &self.children_list + } + fn get_current_widget(&self) -> Option<&SLWidgetNotOption> { + let mut w = self.widget.as_ref(); + if self.path.len() > 0 { + if let Some(first_path) = self.path.first() { + let mut ocurr = self.children_hash.get(first_path); + for i in 1..self.path.len() { + ocurr = ocurr.and_then(|curr| curr.children_hash.as_ref()) + .and_then(|h| self.path.get(i).and_then(|p| h.get(p))); + if let Some(cw) = ocurr.and_then(|c| c.widget.as_ref()) { + w = Some(cw) + } + } + if !self.lastpath.is_empty() { + ocurr = ocurr.and_then(|curr| curr.children_hash.as_ref()) + .and_then(|h| h.get(&self.lastpath)); + if let Some(cw) = ocurr.and_then(|c| c.widget.as_ref()) { + w = Some(cw) + } + } + } + } + return w; + } + fn get_current_children_hash(&self) -> Option<&HashMap>{ + if self.path.len() > 0 { + if let Some(first_path) = self.path.first() { + let mut ocurr = self.children_hash.get(first_path); + for i in 1..self.path.len() { + if let Some(curr) = ocurr { + if let Some(h) = &curr.children_hash { + match self.path.get(i) { + Some(p) => ocurr = h.get(p), + _ => break + } + } else { + break; + } + } else { + break; + } + } + if let Some(curr) = ocurr { + return curr.children_hash.as_ref(); + } + } + } + return Some(&self.children_hash); + } + + + pub fn render_list(&mut self, frame: &mut Frame, area: Rect) { + let l = List::new(self.get_current_item_list().clone()) + .highlight_style(Style::new() + .fg(tailwind::AMBER.c900)) + .highlight_symbol("> ") + .highlight_spacing(ratatui::widgets::HighlightSpacing::Always) + .block( + Block::new() + .borders(Borders::ALL) + .border_type(BorderType::Rounded) + .border_style(Style::new().fg(tailwind::AMBER.c100)) + ); + + frame.render_stateful_widget(l,area, &mut self.state); + } + pub fn render_menu(&mut self, frame: &mut Frame, area: Rect) { + if let Some(f) = self.get_current_widget() { + (f.render)(frame, area) + } + } + pub fn enter(&mut self) { + if let Some(s) = self.state.selected() + .and_then(|i| self.get_current_item_list().get(i)) { + if let Some(ldc) = self.get_current_children_hash().and_then(|h| h.get(s)) { + match ldc.action_type { + ActionType::SubMenu => { + self.path.push(s.to_owned()); + self.lastpath = "".to_owned(); + }, + ActionType::Render => { + self.lastpath = s.to_owned(); + }, + ActionType::None => {} + } + } + } + } + pub fn goback(&mut self)->bool { + if self.path.len()>0 { + if !self.lastpath.is_empty() { + self.lastpath="".to_owned(); + } else { + self.path.pop(); + } + return true; + } + return false; + } + pub fn handle_input(&mut self, kc: ratatui::crossterm::event::KeyCode) -> bool { + if let Some(gcw) = self.get_current_widget() { + return (gcw.handle_input)(kc); + } + false + } + } + + +} + + +pub mod AsyncUpdateAnimeList { + use std::{collections::HashMap, sync::Arc}; + + use ratatui::{buffer::Buffer, layout::Rect, style::{Style, palette::tailwind}, widgets::{Block, Borders, List, ListItem, ListState, Paragraph, StatefulWidget, Widget}}; + + use crate::{source::AnimeSource, utils::utils::{first_capital, hash_sort}}; + + pub enum AsyncAnimeStateStatus { + Empty, + Loading, + Success(HashMap), + Error(String) + } + pub struct AsyncAnimeState { + pub list_state: ListState, + pub status: AsyncAnimeStateStatus, + pub page: usize, + pub res: Option>, + pub animesource: Arc> + } + + impl AsyncAnimeState { + pub fn new(animesource: Arc>) -> Self { + let mut list_state = ListState::default(); + list_state.select_first(); + + Self { + list_state, + status: AsyncAnimeStateStatus::Empty, + page: 1, + res: None, + animesource + } + } + } + pub struct AsyncUpdateAnimeList { + trasmitter_to_app: tokio::sync::mpsc::UnboundedSender, + } + impl AsyncUpdateAnimeList { + pub fn new(trasmitter_to_app: tokio::sync::mpsc::UnboundedSender) -> Self { + Self { + trasmitter_to_app, + } + } + pub fn formatEpisodeIndexTitle(index: usize, title:&str) -> String { + return format!("Ep. {:>4} di {}",index,title); + } + } + impl StatefulWidget for AsyncUpdateAnimeList { + type State = AsyncAnimeState; + + fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) { + let block = Block::default().title(format!(" {} - Page {} ", first_capital(&(state.animesource.get_hostname())), state.page)).borders(Borders::ALL); + match &mut state.status { + AsyncAnimeStateStatus::Empty => { + state.status = AsyncAnimeStateStatus::Loading; + let tx = self.trasmitter_to_app.clone(); + let source = state.animesource.clone(); + let h = source.get_hostname(); + let p = state.page.clone(); + tokio::spawn(async move { + let res = source.listupdated(p).await; + match res { + Ok(o) => { + let mut h = HashMap::new(); + for a in o { + let s = Self::formatEpisodeIndexTitle(a.index,&a.title); + h.insert(s.clone(), a.url.clone()); + } + tx.send(AsyncAnimeStateStatus::Success(h)) + }, + Err(_e) => tx.send(AsyncAnimeStateStatus::Error("Failed to load".to_owned())) + } + }); + Paragraph::new("Starting the request").block(block).render(area, buf); + } + AsyncAnimeStateStatus::Loading => { + Paragraph::new("Loading").block(block.border_style(Style::new().fg(tailwind::AMBER.c200))).render(area, buf); + + } + AsyncAnimeStateStatus::Error(err) => { + Paragraph::new(format!("Error: {}", err)).block(block).render(area, buf); + } + AsyncAnimeStateStatus::Success(res) => { + state.res = Some(res.clone()); + let items = hash_sort(&res); + let list_items: Vec = items.iter() + .map(|a| ListItem::new(format!("{}", a))) + .collect(); + StatefulWidget::render( + List::new(list_items).block(block.border_style(Style::new().fg(ratatui::style::Color::Green))).highlight_symbol(">> "), + area, + buf, + &mut state.list_state + ); + } + } + } + } +} + +pub mod Library { + use std::{collections::HashMap, hash::Hash, sync::Arc}; + + use ratatui::{buffer::Buffer, layout::{Constraint, Layout, Rect}, style::{Style, palette::tailwind}, widgets::{Block, BorderType, Borders, List, ListItem, ListState, Paragraph, StatefulWidget, Widget}}; + + use crate::{library::{self, LibraryItem}, source::{EpisodeListItem, MangaSource}, utils::utils::hash_sort}; + pub struct Library { + pub trasmitter_to_app:tokio::sync::mpsc::UnboundedSender>, + pub library_type: LibraryType + } + impl Library { + pub fn new(library_type: LibraryType,trasmitter_to_app: tokio::sync::mpsc::UnboundedSender>) -> Self { + Self { + library_type, + trasmitter_to_app, + } + } + } + pub enum LibraryStateStatus { + Empty, + Lib(usize), + } + + + pub struct LibraryState { + pub list_state_source: ListState, + pub list_state_eps: ListState, + pub lib: HashMap>, + pub status: LibraryStateStatus, + pub entry_selected: bool, + pub episodes_loaded: Option>, + pub current_source: Option + } + impl LibraryState { + pub fn new() -> Self { + let mut list_state_source = ListState::default(); + let mut list_state_eps = ListState::default(); + list_state_source.select_first(); + list_state_eps.select_first(); + + Self { + lib: HashMap::new(), + list_state_source, + list_state_eps, + entry_selected: false, + status: LibraryStateStatus::Empty, + episodes_loaded: None, + current_source: None + } + } + } + #[derive(Clone,Copy)] + pub enum LibraryType { + Anime, Manga + } + // TODO + impl StatefulWidget for Library { + type State = LibraryState; + + fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) { + let block = Block::default().title("Library").borders(Borders::ALL); + let lay = Layout::default().direction(ratatui::layout::Direction::Horizontal).constraints([ + Constraint::Percentage(100), + Constraint::Min(30) + ]).split(area); + match &mut state.status { + LibraryStateStatus::Empty => { + let f = match self.library_type { + LibraryType::Anime => "animelibrary.toml", + LibraryType::Manga => "mangalibrary.toml" + }; + if let Ok(lib) = library::LibraryItem::buildfromName(f) { + state.lib = lib; + state.status = LibraryStateStatus::Lib(0); + state.entry_selected = false; + return Paragraph::new("Lib loaded").block(block).render(area, buf); + } else { + return Paragraph::new("No lib found. Edit ./library/animelibrary.toml or ./library/mangalibrary.toml. Format:\n[[Animeworld]]\nname=\"Frieren\"\nepisodes=\"https://url_to_a_generic_episode_of_animeworld_frieren_or_chapter_list_manga_page/\"").block(block).render(area, buf); + } + } + LibraryStateStatus::Lib(index) => { + let mut keys: Vec<&String> = state.lib.keys().collect(); + keys.sort(); + let i = index.clone(); + let l = keys.len(); + let block_sc = Block::new() + .borders(Borders::ALL) + .border_type(BorderType::Rounded) + .border_style(Style::new().fg(ratatui::style::Color::Cyan)); + let block_eps = Block::new() + .borders(Borders::ALL) + .border_type(BorderType::Rounded) + .border_style(Style::new().fg(ratatui::style::Color::Cyan)); + if let Some(k) = keys.get(i % l) { + let key = *k; + if let Some(a) = state.lib.get(key) { + state.current_source = Some(key.clone()); + let list = List::new(a.iter().map(|li| ListItem::new(li.name.clone())).collect::>()).block(block_sc); + StatefulWidget::render(list, lay[0], buf, &mut state.list_state_source); + } else { + Widget::render(block_sc, lay[0], buf); + } + } else { + Widget::render(block_sc, lay[0], buf); + } + if state.entry_selected && let Some(eps_hash) = &state.episodes_loaded { + let mut items = eps_hash.keys().collect::>(); + items.sort_by(|a,b| b.cmp(a)); // ? REVERSE SORT - MAX TO MIN -> NEWEST FIRST + let mut eps = Vec::with_capacity(items.len()); + for it in items { + eps.push(ListItem::new(it.clone())); + } + let list_ep = List::new(eps).block(block_eps); + StatefulWidget::render(list_ep, lay[1], buf, &mut state.list_state_eps); + } else { + Widget::render(block_eps, lay[1], buf); + } + } + } + } + } +} + +pub mod AsyncUpdateMangaList { + use std::{collections::HashMap, sync::Arc}; + + use ratatui::{buffer::Buffer, layout::Rect, style::{Style, palette::tailwind}, widgets::{Block, Borders, List, ListItem, ListState, Paragraph, StatefulWidget, Widget}}; + + use crate::{source::{MangaSource}, utils::utils::{first_capital, hash_sort}}; + + pub enum AsyncMangaStateStatus { + Empty, + Loading, + Success(HashMap), + Error(String) + } + pub struct AsyncMangaState { + pub list_state: ListState, + pub status: AsyncMangaStateStatus, + pub page: usize, + pub res: Option>, + pub mangasource: Arc> + } + + impl AsyncMangaState { + pub fn new(mangasource: Arc>) -> Self { + let mut list_state = ListState::default(); + list_state.select_first(); + + Self { + list_state, + status: AsyncMangaStateStatus::Empty, + page: 1, + res: None, + mangasource + } + } + } + pub struct AsyncUpdateMangaList { + trasmitter_to_app: tokio::sync::mpsc::UnboundedSender, + } + impl AsyncUpdateMangaList { + pub fn new(trasmitter_to_app: tokio::sync::mpsc::UnboundedSender) -> Self { + Self { + trasmitter_to_app, + } + } + pub fn formatEpisodeIndexTitle(index: usize, title:&str) -> String { + return format!("Ep. {:>4} di {}",index,title); + } + } + impl StatefulWidget for AsyncUpdateMangaList { + type State = AsyncMangaState; + + fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) { + let block = Block::default().title(format!(" {} - Page {} ", first_capital(&(state.mangasource.get_hostname())), state.page)).borders(Borders::ALL); + match &mut state.status { + AsyncMangaStateStatus::Empty => { + state.status = AsyncMangaStateStatus::Loading; + let tx = self.trasmitter_to_app.clone(); + let source = state.mangasource.clone(); + let h = source.get_hostname(); + let p = state.page.clone(); + tokio::spawn(async move { + let res = source.listupdated(p).await; + match res { + Ok(o) => { + let mut h = HashMap::new(); + for a in o { + let s = Self::formatEpisodeIndexTitle(a.index,&a.title); + h.insert(s.clone(), a.url.clone()); + } + tx.send(AsyncMangaStateStatus::Success(h)) + }, + Err(_e) => tx.send(AsyncMangaStateStatus::Error("Failed to load".to_owned())) + } + }); + Paragraph::new("Starting the request").block(block).render(area, buf); + } + AsyncMangaStateStatus::Loading => { + Paragraph::new("Loading").block(block.border_style(Style::new().fg(tailwind::AMBER.c200))).render(area, buf); + + } + AsyncMangaStateStatus::Error(err) => { + Paragraph::new(format!("Error: {}", err)).block(block).render(area, buf); + } + AsyncMangaStateStatus::Success(res) => { + state.res = Some(res.clone()); + let items = hash_sort(&res); + let list_items: Vec = items.iter() + .map(|a| ListItem::new(format!("{}", a))) + .collect(); + StatefulWidget::render( + List::new(list_items).block(block.border_style(Style::new().fg(ratatui::style::Color::Green))).highlight_symbol(">> "), + area, + buf, + &mut state.list_state + ); + } + } + } + } +} \ No newline at end of file diff --git a/src/config.md b/src/config.md new file mode 100644 index 0000000..c39bca4 --- /dev/null +++ b/src/config.md @@ -0,0 +1,83 @@ +use std::{default, fs}; +use ratatui::style::palette::tailwind; +use serde::Deserialize; +use std::sync::OnceLock; + + + +#[derive(Deserialize)] +pub struct Config { + pub Settings: Settings, + pub Customization: Customization +} + + +#[derive(Deserialize)] +pub struct Settings { + pub Folders: Folders +} + +#[derive(Deserialize)] +pub struct Folders { + pub basedir: String, + pub extensions: String, + pub anime: String, + pub manga: String +} +#[derive(Deserialize)] +pub struct Customization { + pub ColorPalette: ColorPalette +} +#[derive(Deserialize)] +pub struct ColorPalette { + pub anime: String, + pub manga: String, + pub source: String, + pub settings: String +} + + + + +static CONFIG: OnceLock = OnceLock::new(); + +pub fn getConfig() -> &'static Config { + CONFIG.get().expect("No init") +} + +pub fn load_config() { + let content = fs::read_to_string("config.toml").expect("Can't read config file"); + let parsed:Config = toml::from_str(&content).unwrap(); + CONFIG.set(parsed).ok(); + // Ok(()) +} + + +pub fn tailwindPaletteFromString(c: &str, def: tailwind::Palette) -> tailwind::Palette { + let res = match c { + "AMBER" => tailwind::AMBER, + "BLUE" => tailwind::BLUE, + "CYAN" => tailwind::CYAN, + "EMERALD" => tailwind::EMERALD, + "FUCHSIA" => tailwind::FUCHSIA, + "GRAY" => tailwind::GRAY, + "GREEN" => tailwind::GREEN, + "INDIGO" => tailwind::INDIGO, + "LIME" => tailwind::LIME, + "NEUTRAL" => tailwind::NEUTRAL, + "ORANGE" => tailwind::ORANGE, + "PINK" => tailwind::PINK, + "PURPLE" => tailwind::PURPLE, + "RED" => tailwind::RED, + "ROSE" => tailwind::ROSE, + "SKY" => tailwind::SKY, + "SLATE" => tailwind::SLATE, + "STONE" => tailwind::STONE, + "TEAL" => tailwind::TEAL, + "VIOLET" => tailwind::VIOLET, + "YELLOW" => tailwind::YELLOW, + "ZINC" => tailwind::ZINC, + _ => def + }; + res +} \ No newline at end of file diff --git a/src/extension.rs b/src/extension.rs new file mode 100644 index 0000000..d553043 --- /dev/null +++ b/src/extension.rs @@ -0,0 +1,69 @@ +use std::fs; + +use std::result::Result::Ok; +use serde::Deserialize; + +use crate::source::{AnimeSource, MangaSource, SourceType}; +use crate::source::animeworld::Animeworld; +use crate::source::mangaworld::Mangaworld; + +#[derive(Deserialize, Clone)] +pub struct Extension { + Type: String, + Info: ExtensionInfo, + Sitemanager: ExtensionSitemanager +} +#[derive(Deserialize, Clone)] +struct ExtensionInfo { + Name: String, + Description: String, + Language: String +} + +#[derive(Deserialize, Clone)] +struct ExtensionSitemanager { + Hostname: String, + Url: String, + +} + +impl Extension { + pub fn buildfromName(kind: SourceType,name: &str) -> Result { + let f = format!("extensions/{}/{}.toml",kind.to_string(), name.to_lowercase()); + if let Ok(exists)= fs::exists(&f) { + if exists { + if let Ok(content)= fs::read_to_string(&f) { + let parsed:Extension = toml::from_str(&content).unwrap(); + return Ok(parsed); + } + } + } + return Err(name); + } + pub fn toAnimeSource(self) -> Box { + match self.Info.Name.trim().to_lowercase().as_str() { + // "animeworld" => Animeworld::new(self.Sitemanager.Hostname, self.Sitemanager.Url), + _ => Box::new(Animeworld::new(self.Sitemanager.Hostname, self.Sitemanager.Url)) + } + } + pub fn toMangaSource(self) -> Box { + match self.Info.Name.trim().to_lowercase().as_str() { + // "animeworld" => Animeworld::new(self.Sitemanager.Hostname, self.Sitemanager.Url), + _ => Box::new(Mangaworld::new(self.Sitemanager.Hostname, self.Sitemanager.Url)), + } + } + pub fn defaultAnimeSource() -> Result, String> { + let def = "animeworld"; + if let Ok(ext) = Extension::buildfromName(SourceType::Anime, def) { + return Ok(ext.toAnimeSource()); + } + return Err(format!("No default file for {}",def)); + } + pub fn defaultMangaeSource() -> Result, String> { + let def = "mangaworld"; + if let Ok(ext) = Extension::buildfromName(SourceType::Anime, def) { + return Ok(ext.toMangaSource()); + } + return Err(format!("No default file for {}",def)); + } +} \ No newline at end of file diff --git a/src/folders.rs b/src/folders.rs new file mode 100644 index 0000000..23d0de2 --- /dev/null +++ b/src/folders.rs @@ -0,0 +1,67 @@ +use std::{ffi::OsString, fs::{create_dir_all, read_dir}}; +use serde::Deserialize; + +// const DOWNLOADS: &str = "/downloads"; +const ANIME_FOLDER: &str = "./downloads/anime"; +const MANGA_FOLDER: &str = "./downloads/manga"; + +pub fn init_folders() { + let _ = create_dir_all(ANIME_FOLDER); + let _ = create_dir_all(MANGA_FOLDER); +} + + +pub fn list_folder(folder: &str) -> Result, Box> { + let r = read_dir(folder)? + .filter_map(|entry| entry.ok()) + .filter(|entry| entry.path().is_dir()) + .map(|entry| entry.file_name()) + .collect::>(); + Ok(r) +} +pub fn read_anime_folder() -> Result, Box> { + list_folder(ANIME_FOLDER) +} +pub fn read_manga_folder() -> Result, Box> { + list_folder(MANGA_FOLDER) +} + +#[derive(Deserialize)] +pub struct Config { + pub Settings: Settings, + pub Extension: Extensions, +} + + +#[derive(Deserialize)] +pub struct Settings { + pub Folders: Folders +} +#[derive(Deserialize)] +pub struct Extensions { + pub defaultanime: String, + pub defaultmanga: String +} +#[derive(Deserialize)] +pub struct Folders { + pub basedir: String, + pub extensions: String, + pub anime: String, + pub manga: String +} +#[derive(Deserialize)] +pub struct ColorPalette { + pub anime: String, + pub manga: String, + pub source: String, + pub settings: String +} + + + + +// impl Config { +// pub fn defaultAnimeSource() -> Source { +// Animeworld::new(hostname, url, sourcetype) +// } +// } \ No newline at end of file diff --git a/src/keybinds.md b/src/keybinds.md new file mode 100644 index 0000000..d3d8178 --- /dev/null +++ b/src/keybinds.md @@ -0,0 +1,116 @@ +use std::{fs, u8}; +use ratatui::crossterm::event::KeyCode; +use serde::Deserialize; +#[derive(Deserialize)] +pub struct KeybindsString { + pub GoBack:String, + pub Open:String, + pub MenuUp:String, + pub MenuDown:String, + pub MenuLeft:String, + pub MenuRight:String, + pub AddFavourite:String, +} +pub struct Keybinds { + pub GoBack:KeyCode, + pub Open:KeyCode, + pub MenuUp:KeyCode, + pub MenuDown:KeyCode, + pub MenuLeft:KeyCode, + pub MenuRight:KeyCode, + pub AddFavourite:KeyCode, +} +impl KeybindsString { + pub fn build() -> Result { + let f = "keybinds.toml"; + if let Ok(exists)= fs::exists(f) { + if exists { + if let Ok(content)= fs::read_to_string(f) { + let parsed:KeybindsString = toml::from_str(&content).unwrap(); + return Ok(parsed); + } + } + } + return Err(f.to_owned()); + } +} +impl Keybinds { + pub fn build() -> Result { + let x: Result = match KeybindsString::build() { + Ok(ks) => Ok( + Self { + GoBack: ks.GoBack.to_key_code()?, + Open: ks.Open.to_key_code()?, + MenuUp: ks.MenuUp.to_key_code()?, + MenuDown: ks.MenuDown.to_key_code()?, + MenuLeft: ks.MenuLeft.to_key_code()?, + MenuRight: ks.MenuRight.to_key_code()?, + AddFavourite: ks.AddFavourite.to_key_code()?, + } + ), + Err(f) => Err(f), + }; + x + } + pub fn buildOrDefault() -> Result { + let x: Result = match KeybindsString::build() { + Ok(ks) => Ok( + Self { + GoBack: ks.GoBack.to_key_code().unwrap_or(KeyCode::Esc), + Open: ks.Open.to_key_code().unwrap_or(KeyCode::Enter), + MenuUp: ks.MenuUp.to_key_code().unwrap_or(KeyCode::Up), + MenuDown: ks.MenuDown.to_key_code().unwrap_or(KeyCode::Down), + MenuLeft: ks.MenuLeft.to_key_code().unwrap_or(KeyCode::Left), + MenuRight: ks.MenuRight.to_key_code().unwrap_or(KeyCode::Right), + AddFavourite: ks.AddFavourite.to_key_code().unwrap_or(KeyCode::Char('p')), + } + ), + Err(f) => Err(f), + }; + x + } +} +trait StringToKeyCode { + fn to_key_code(self) -> Result; +} + +impl StringToKeyCode for String { + fn to_key_code(self) -> Result { + match self.to_uppercase().as_ref() { + "BACKSPACE"=>Ok(KeyCode::Backspace), + "ENTER"=>Ok(KeyCode::Enter), + "LEFT"=>Ok(KeyCode::Left), + "RIGHT"=>Ok(KeyCode::Right), + "UP"=>Ok(KeyCode::Up), + "DOWN"=>Ok(KeyCode::Down), + "HOME"=>Ok(KeyCode::Home), + "END"=>Ok(KeyCode::End), + "PAGEUP"=>Ok(KeyCode::PageUp), + "PAGEDOWN"=>Ok(KeyCode::PageDown), + "TAB"=>Ok(KeyCode::Tab), + "BACKTAB"=>Ok(KeyCode::BackTab), + "DELETE"=>Ok(KeyCode::Delete), + "INSERT"=>Ok(KeyCode::Insert), + "ESC"=>Ok(KeyCode::Esc), + "CAPSLOCK"=>Ok(KeyCode::CapsLock), + "SCROLLLOCK"=>Ok(KeyCode::ScrollLock), + "NUMLOCK"=>Ok(KeyCode::NumLock), + "PRINTSCREEN"=>Ok(KeyCode::PrintScreen), + "PAUSE"=>Ok(KeyCode::Pause), + "MENU"=>Ok(KeyCode::Menu), + "KEYPADBEGIN"=>Ok(KeyCode::KeypadBegin), + + a => { + let chars: Vec = a.chars().collect(); + return match chars.as_slice() { + [c] => Ok(KeyCode::Char(*c)), + ['F', d] if d.is_ascii_digit() =>{ + let dg = d.to_digit(10).unwrap() as u8; + Ok(KeyCode::F(dg)) + }, + _ => Err(format!("{} is invalid", self)) + }; + } + } + } +} \ No newline at end of file diff --git a/src/library.rs b/src/library.rs new file mode 100644 index 0000000..a058620 --- /dev/null +++ b/src/library.rs @@ -0,0 +1,38 @@ +use std::collections::{HashMap, HashSet}; +use std::fs; + +use std::result::Result::Ok; +use serde::Deserialize; + + + +#[derive(Deserialize,Clone)] +pub struct LibraryItem { + pub name: String, + pub episodes: String +} + + +impl LibraryItem { + pub fn buildfromName(name: &str) -> Result>, &str> { + let f = format!("library/{}", name); + if let Ok(exists)= fs::exists(&f) { + if exists { + if let Ok(content)= fs::read_to_string(&f) { + let mut data: HashMap> = toml::from_str(&content).unwrap(); + for items in data.values_mut() { + let mut hs = HashSet::new(); + items.retain(|item| { + hs.insert(item.name.clone()) + }); + items.sort_by(|a,b| { + a.name.cmp(&b.name) + }); + } + return Ok(data); + } + } + } + return Err(name); + } +} \ No newline at end of file diff --git a/src/main.md b/src/main.md new file mode 100755 index 0000000..9910833 --- /dev/null +++ b/src/main.md @@ -0,0 +1,233 @@ +mod config; +mod mangatab; +mod extensions; +use color_eyre::{Result}; +use ratatui::{ + DefaultTerminal, buffer::Buffer, crossterm::event::{self, Event, KeyCode, KeyEventKind, KeyModifiers}, layout::{Constraint, Layout, Rect}, macros::horizontal, style::{Color, Stylize, palette::tailwind}, text::{Line, Text}, widgets::{Block, List, ListDirection, ListItem, Padding, Paragraph, Tabs, Widget} +}; +use strum::{Display, EnumIter, FromRepr, IntoEnumIterator}; +use crate::{mangatab::MangaTab, config::{getConfig, load_config, tailwindPaletteFromString}}; + +#[tokio::main] +async fn main() -> Result<()> { + load_config(); + + color_eyre::install()?; + let terminal = ratatui::init(); + let app_result = App::default().run(terminal); + ratatui::restore(); + app_result +} + +#[derive(Default)] +struct App { + state: AppState, + selected_tab: SelectedTab +} + +#[derive(Default, Clone, Copy, PartialEq, Eq)] +enum AppState { + #[default] + Running, + Quitting, +} + +#[derive(Default, Clone, Copy, Display, FromRepr, EnumIter)] +enum SelectedTab { + #[default] + #[strum(to_string = "Anime")] + Tab1, + #[strum(to_string = "Manga")] + Tab2, + #[strum(to_string = "Source")] + Tab3, + #[strum(to_string = "Settings")] + Tab4, +} + + +impl App { + fn run(mut self, mut terminal: DefaultTerminal) -> Result<()> { + while self.state == AppState::Running { + terminal.draw(|frame| frame.render_widget(&self, frame.area()))?; + self.handle_events()?; + } + Ok(()) + } + + fn handle_events(&mut self) -> std::io::Result<()> { + if let Event::Key(key) = event::read()? { + if key.kind == KeyEventKind::Press { + match key.code { + KeyCode::Esc => self.quit(), + _ => {} + } + if key.modifiers.contains(KeyModifiers::CONTROL) { + match key.code { + KeyCode::Char('c') => self.quit(), + _=>{} + } + } + if key.modifiers.contains(KeyModifiers::SHIFT) { + match key.code { + KeyCode::Right => self.next_tab(), + KeyCode::Left => self.previous_tab(), + _ => {} + } + } else { + + } + } + } + Ok(()) + } + + pub fn next_tab(&mut self) { + self.selected_tab = self.selected_tab.next(); + } + + pub fn previous_tab(&mut self) { + self.selected_tab = self.selected_tab.previous(); + } + + pub fn subnext_tab(&mut self) { + self.selected_tab = self.selected_tab.subnext(); + } + + pub fn subprevious_tab(&mut self) { + self.selected_tab = self.selected_tab.subprevious(); + } + + pub fn quit(&mut self) { + self.state = AppState::Quitting; + } +} + +impl SelectedTab { + /// Get the previous tab, if there is no previous tab return the current tab. + fn previous(self) -> Self { + let current_index: usize = self as usize; + let previous_index = current_index.saturating_sub(1); + Self::from_repr(previous_index).unwrap_or(self) + } + + /// Get the next tab, if there is no next tab return the current tab. + fn next(self) -> Self { + let current_index = self as usize; + let next_index = current_index.saturating_add(1); + Self::from_repr(next_index).unwrap_or(self) + } + +} + +impl Widget for &App { + fn render(self, area: Rect, buf: &mut Buffer) { + use Constraint::{Length, Min}; + let vertical = Layout::vertical([Length(1), Min(0), Length(1)]); + let [header_area, inner_area, footer_area] = vertical.areas(area); + + let horizontal = Layout::horizontal([Min(0), Length(20)]); + let [tabs_area, title_area] = horizontal.areas(header_area); + + render_title(title_area, buf); + self.render_tabs(tabs_area, buf); + self.selected_tab.render(inner_area, buf); + render_footer(footer_area, buf); + } +} + +impl App { + fn render_tabs(&self, area: Rect, buf: &mut Buffer) { + let titles = SelectedTab::iter().map(SelectedTab::title); + let highlight_style = (Color::default(), self.selected_tab.palette().c700); + let selected_tab_index = self.selected_tab as usize; + Tabs::new(titles) + .highlight_style(highlight_style) + .select(selected_tab_index) + .padding("", "") + .divider(" ") + .render(area, buf); + } +} + +fn render_title(area: Rect, buf: &mut Buffer) { + "Anime&Manga Scraper CLI".bold().render(area, buf); +} + +fn render_footer(area: Rect, buf: &mut Buffer) { + Line::raw("Shift + ArrowKeys to choose the Tab | Press ESC to quit") + .centered() + .render(area, buf); +} + +impl Widget for SelectedTab { + fn render(self, area: Rect, buf: &mut Buffer) { + // in a real app these might be separate widgets + match self { + Self::Tab1 => self.render_Anime(area, buf), + Self::Tab2 => self.render_Manga(area, buf), + Self::Tab3 => self.render_Sources(area, buf), + Self::Tab4 => self.render_Settings(area, buf), + } + } +} + +impl SelectedTab { + /// Return tab's name as a styled `Line` + fn title(self) -> Line<'static> { + format!(" {self} ") + .fg(tailwind::SLATE.c200) + // .bg(self.palette().c900) + .into() + } + + fn render_Anime(self, area: Rect, buf: &mut Buffer) { + Paragraph::new("Anime Home Library: View saved and search for new one in all sources") + .block(self.block()) + .render(area, buf); + } + + fn render_Manga(self, area: Rect, buf: &mut Buffer) { + // Paragraph::new("Manga Home Library: View saved and search for new one in all sources") + // .block(self.block()) + // .render(area, buf); + // INNER TAB FOR OPTIONS + let titles = MangaTab::iter().map(MangaTab::title); + let highlight_style = (Color::default(), self.selected_tab.palette().c700); + let selected_tab_index = self.selected_tab as usize; + Tabs::new(titles) + .highlight_style(highlight_style) + .select(selected_tab_index) + .padding("", "") + .divider(" ") + .render(area, buf); + } + fn render_Sources(self, area: Rect, buf: &mut Buffer) { + Paragraph::new("Sources to source the web") + .block(self.block()) + .render(area, buf); + } + + fn render_Settings(self, area: Rect, buf: &mut Buffer) { + Paragraph::new("For now just credits") + .block(self.block()) + .render(area, buf); + } + + + /// A block surrounding the tab's content + fn block(self) -> Block<'static> { + Block::bordered() + .padding(Padding::horizontal(1)) + .border_style(self.palette().c500) + } + + fn palette(self) -> tailwind::Palette { + match self { + Self::Tab1 => tailwindPaletteFromString(&getConfig().Customization.ColorPalette.anime,tailwind::PURPLE), + Self::Tab2 => tailwindPaletteFromString(&getConfig().Customization.ColorPalette.manga,tailwind::AMBER), + Self::Tab3 => tailwindPaletteFromString(&getConfig().Customization.ColorPalette.source,tailwind::EMERALD), + Self::Tab4 => tailwindPaletteFromString(&getConfig().Customization.ColorPalette.settings,tailwind::RED), + } + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..8a902bc --- /dev/null +++ b/src/main.rs @@ -0,0 +1,587 @@ +// mod config; +// mod mangatab; +mod extension; +mod library; +mod components; +mod source; +mod utils; +mod folders; +use std::{any::Any, cell::RefCell, collections::HashMap, process::{Command, Stdio}, rc::Rc, sync::Arc, time::Duration, vec}; +use color_eyre::{Result}; +use ratatui::{ + DefaultTerminal, Frame, crossterm::event::{self, Event, KeyCode, KeyEventKind, KeyModifiers}, layout::{Constraint, Direction, Layout, Rect}, style::Style, text::{Line, Span}, widgets::{Block, BorderType, Borders, List, ListItem, ListState, Paragraph} +}; +use crate::{components::{AsyncUpdateAnimeList::{self, AsyncAnimeState, AsyncAnimeStateStatus}, AsyncUpdateMangaList::{AsyncMangaState, AsyncMangaStateStatus}, Library::{self, LibraryState, LibraryStateStatus}, list::{ActionType, CurrentWidget, ListDiveChild, SelectableList}}, extension::Extension, source::{AVAIBLEANIMESOURCE, AnimeSource, EpisodeListItem, MangaSource, SourceType, animeworld, mangaworld}}; +//use crate::{mangatab::MangaTab, config::{getConfig, load_config, tailwindPaletteFromString}}; + +#[tokio::main] +async fn main() -> Result<()> { + //load_config(); + folders::init_folders(); + color_eyre::install()?; + let terminal = ratatui::init(); + let app_result = App::new().run(terminal).await; + ratatui::restore(); + app_result +} + +//#[derive(Default)] +struct App { + state: AppState, + navigation_menu: SelectableList, + // pub anime_list: components::asyncListWidget::AsyncListWidget, + anime: AnimeApp, + manga: MangaApp, + library: LibraryApp +} +struct AnimeApp { + anime_source: Arc>, + tx: tokio::sync::mpsc::UnboundedSender, + rx: tokio::sync::mpsc::UnboundedReceiver, + anime_list_state: Rc> +} +struct MangaApp { + manga_source: Arc>, + tx: tokio::sync::mpsc::UnboundedSender, + rx: tokio::sync::mpsc::UnboundedReceiver, + manga_list_state: Rc> +} +struct LibraryApp { + tx: tokio::sync::mpsc::UnboundedSender>, + rx: tokio::sync::mpsc::UnboundedReceiver>, + library_list_state: Rc> +} + + +#[derive(Clone, Copy, PartialEq, Eq)] +enum AppState { + Running, + Quitting, +} +impl App { + fn new()->App { + let (tx_a, rx_a) = tokio::sync::mpsc::unbounded_channel::(); + let (tx_m, rx_m) = tokio::sync::mpsc::unbounded_channel::(); + let (tx_l, rx_l) = tokio::sync::mpsc::unbounded_channel::>(); + let anime_source:Arc> = Arc::new(Extension::defaultAnimeSource().unwrap_or(Box::new(animeworld::Animeworld::new("animeworld.ac".to_owned(), "https://animeworld.ac".to_owned())))); // TODO Make anime source selectable + let manga_source:Arc> = Arc::new(Extension::defaultMangaeSource().unwrap_or(Box::new(mangaworld::Mangaworld::new("mangaworld.mx".to_owned(), "https://mangaworld.mx".to_owned())))); // TODO Make anime source selectable + let a_s = anime_source.clone(); + let m_s = manga_source.clone(); + Self { + state: AppState::Running, + navigation_menu: SelectableList::default(), + anime: AnimeApp { + anime_source, + anime_list_state: Rc::new(RefCell::new(AsyncAnimeState::new(a_s))), + tx: tx_a, + rx: rx_a + }, + manga: MangaApp { + manga_source, + tx: tx_m, + rx: rx_m, + manga_list_state: Rc::new(RefCell::new(AsyncMangaState::new(m_s))) + }, + library: LibraryApp { + tx: tx_l, + rx: rx_l, + library_list_state: Rc::new(RefCell::new(LibraryState::new())) + } + // anime_list: AsyncListWidget { + // title: "Animeworld".into(), + // state: AsyncState::Empty, + // scroll_state: ListState::default(), + // animeurlhash: HashMap::new() + // }, + } + } + fn withAnimeSource(&mut self, a: &str) { + if let Ok(nas) = extension::Extension::buildfromName(source::SourceType::Anime, a) { + let anime_s = nas.toAnimeSource(); + self.anime.anime_list_state=Rc::new(RefCell::new(AsyncAnimeState::new(Arc::new(anime_s)))) + } + + + } + fn withMangaSource(&mut self, m: Arc>) { + + } + fn init(&mut self) { + // let tx_clone = self.tx.clone(); + self.navigation_menu = SelectableList::new(HashMap::from([ + ("Anime".to_owned(), ListDiveChild::new( + "Anime".to_owned(), + 0, + ActionType::SubMenu, + Some(HashMap::from([ + ("Nuovi".to_owned(), ListDiveChild::new( + "Nuovi".to_owned(), + 0, + ActionType::Render, + None, + Some(self.block_anime_updated()), + )), + ("Libreria".to_owned(), ListDiveChild::new( + "Libreria".to_owned(), + 1, + ActionType::Render, + None, + Some(self.block_library(Library::LibraryType::Anime))//Some(self.new_anime_list()), + + )) + ])), + None, + )), + ("Manga".to_owned(), ListDiveChild::new( + "Manga".to_owned(), + 1, + ActionType::SubMenu, + Some(HashMap::from([ + ("Library".to_owned(), ListDiveChild::new( + "Library".to_owned(), + 0, + ActionType::Render, + None, + Some(self.block_library(Library::LibraryType::Manga)) + )), + ("Search".to_owned(), ListDiveChild::new( + "Search".to_owned(), + 1, + ActionType::Render, + None, + None, + )) + ])), + None, + )), + // ("Settings".to_owned(), ListDiveChild::new( + // "Settings".to_owned(), 2, ActionType::SubMenu, Some(HashMap::from([ + // ("Source".to_owned(), ListDiveChild::new( + // "Source".to_owned(), + // 0, + // ActionType::Render, + // None, + // Some(self.settings_selectSource()) + // )) + // ])), None)) + ]),Some(Self::generate_block())); + } + + fn update(&mut self) { + match self.anime.rx.try_recv() { + Ok(a) => { + self.anime.anime_list_state.borrow_mut().status=a; + }, + Err(_) => {} + }; + match self.manga.rx.try_recv() { + Ok(a) => { + self.manga.manga_list_state.borrow_mut().status=a; + }, + Err(_) => {} + }; + match self.library.rx.try_recv() { + Ok(h)=>{ + self.library.library_list_state.borrow_mut().episodes_loaded = Some(h); + } + Err(_) => {} + } + } + async fn run(&mut self, mut terminal: DefaultTerminal) -> Result<()> { + self.init(); + while self.state == AppState::Running { + self.update(); + // log!("Draw"); + terminal.draw(|frame| self.draw(frame))?; + self.handle_events()?; + } + Ok(()) + } + + fn handle_events(&mut self) -> Result<()> { + if event::poll(Duration::from_millis(500))? { + if let Event::Key(key) = event::read()? { + if key.kind == KeyEventKind::Press { + match key.code { + k @ (KeyCode::Esc | KeyCode::Backspace) => { + if !self.navigation_menu.handle_input(k) { + if !self.navigation_menu.goback() { + self.quit() + } + } + }, + // KeyCode::Backspace => {self.navigation_menu.goback();}, + k @ (KeyCode::Up | KeyCode::Char('w'))=> { + if self.navigation_menu.lastpath.is_empty() { + self.navigation_menu.state.select_previous(); + } else { + self.navigation_menu.handle_input(k); + } + }, + k @ (KeyCode::Down | KeyCode::Char('s'))=> { + if self.navigation_menu.lastpath.is_empty() { + self.navigation_menu.state.select_next(); + } else { + self.navigation_menu.handle_input(k); + } + } + KeyCode::Enter => { + if self.navigation_menu.lastpath.is_empty() { + self.navigation_menu.enter(); + } else { + self.navigation_menu.handle_input(KeyCode::Enter); + } + }, + a => { + if !self.navigation_menu.lastpath.is_empty() { + self.navigation_menu.handle_input(a); + } + } + } + if key.modifiers.contains(KeyModifiers::CONTROL) { + match key.code { + KeyCode::Char('c') => self.quit(), + _=>{} + } + } + } + } + } + Ok(()) + } + pub fn quit(&mut self) { + self.state = AppState::Quitting; + } + + fn draw(&mut self, frame: &mut Frame) { + let lay = Layout::default() + .direction(Direction::Horizontal) + .margin(1) + .constraints(vec![ + Constraint::Percentage(15), + Constraint::Percentage(85), + ]) + .split(frame.area()); + self.navigation_menu.render_list(frame, lay[0]); + // self.navigation_menu(frame, lay[1]); + self.navigation_menu.render_menu(frame, lay[1]); + } +} + +/** + * Widget inside app + */ +pub enum CmdListType { + Single,Multi +} +impl App { + pub fn generate_cmd_list(list: Vec<(CmdListType,&str, &str)>) -> Paragraph<'static> { + let b = Block::new() + .borders(Borders::ALL) + .border_type(BorderType::Rounded) + .border_style(Style::new().fg(ratatui::style::Color::LightCyan)); + let mut string_v:Vec> = Vec::with_capacity(list.len()*5); + let cmd_style = Style::new().fg(ratatui::style::Color::LightCyan); + let text_style = Style::new().fg(ratatui::style::Color::White); + for l in list { + match l.0 { + CmdListType::Multi => { + for key in l.1.split(" or ") { + let mut sp= key.split(","); + if let Some(first) = sp.next() { + string_v.push(Span::styled("[",text_style)); + string_v.push(Span::styled(first.to_owned(),cmd_style)); + while let Some(k) = sp.next() { + string_v.push(Span::styled(",",text_style)); + string_v.push(Span::styled(k.to_string(),cmd_style)); + } + string_v.push(Span::styled("]",text_style)); + string_v.push(Span::styled(" or ",text_style)); + } + } + string_v.pop(); + string_v.push(Span::styled(" ",text_style)); + string_v.push(Span::styled(l.2.to_owned(),text_style)); + }, + CmdListType::Single => { + string_v.push(Span::styled("[",text_style)); + string_v.push(Span::styled(l.1.to_owned(),cmd_style)); + string_v.push(Span::styled("] ",text_style)); + string_v.push(Span::styled(l.2.to_owned(),text_style)); + } + } + string_v.push(Span::styled(";",text_style)); + string_v.push(Span::styled(" ",text_style)); + } + string_v.pop(); + let line: Line<'_> = Line::from(string_v); + Paragraph::new(line).block(b).centered() + // Paragraph::new(String::from("[W,A,S,D] or [Arrows] to move;\t[Enter] select;\t[Esc] Go Back")).block(b).centered() + } + pub fn generate_block() -> CurrentWidget { + CurrentWidget { + render: Box::new(|frame: &mut Frame, area: Rect| { + let b = Block::new() + .borders(Borders::ALL) + .border_type(BorderType::Rounded) + .border_style(Style::new().fg(ratatui::style::Color::Cyan)); + let lay = Layout::default() + .direction(Direction::Vertical) + .constraints(vec![ + Constraint::Percentage(100), + Constraint::Min(3) + ]).split(area); + let cmd_list = Self::generate_cmd_list(vec![ + (CmdListType::Multi, "W,A,S,D or Arrows","to move"), + (CmdListType::Single, "Enter","select"), + (CmdListType::Single, "Esc","Go Back"), + ]); + frame.render_widget(b, lay[0]); + frame.render_widget(cmd_list, lay[1]); + }), + handle_input: Box::new(|_| {false}) + } + } + pub fn block_anime_updated(&mut self) -> CurrentWidget { + // let ls =.borrow_mut(); // + let render_ls = Rc::clone(&self.anime.anime_list_state); + let input_ls = Rc::clone(&self.anime.anime_list_state); + let tx = self.anime.tx.clone(); + // let sc = self.anime_source.clone(); + CurrentWidget { + render: Box::new(move |frame: &mut Frame, area: Rect| { + let b = Block::default() + .borders(Borders::ALL) + .border_type(BorderType::Rounded) + .border_style(Style::new().fg(ratatui::style::Color::LightCyan)); + let lay = Layout::default() + .direction(Direction::Vertical) + .constraints(vec![ + Constraint::Percentage(100), + Constraint::Min(3) + ]).split(area); + // let fld = match read_anime_folder() { + // Ok(a) => a, + // _ => vec::Vec::new() + // }; + // let items = fld.iter().enumerate().map(|(i, folder)| { + // ListItem::new(folder.to_string_lossy().into_owned()) + // }); + let mut state = render_ls.borrow_mut(); + let tx_clone = tx.clone(); + + let l = AsyncUpdateAnimeList::AsyncUpdateAnimeList::new(tx_clone);//AsyncListWidget::new("Animeworld".to_owned(), self.animeSource); + + frame.render_stateful_widget( l, lay[0], &mut *state); + let cmd_list = Self::generate_cmd_list(vec![ + (CmdListType::Multi, "W,A,S,D or Arrows","to move"), + (CmdListType::Single, "Enter","select"), + (CmdListType::Single, "Esc","Go Back"), + ]); //// NOT WORK with match: Keybinds configuration from file; + frame.render_widget( cmd_list, lay[1]); + + }), + handle_input: Box::new(move |k: KeyCode| { + let mut state = input_ls.borrow_mut(); + match state.status { + AsyncAnimeStateStatus::Success(_) => match k { + KeyCode::Up | KeyCode::Char('w') => { + state.list_state.select_previous(); + true + }, + KeyCode::Down | KeyCode::Char('s')=> { + state.list_state.select_next(); + true + }, + KeyCode::Left | KeyCode::Char('a') => { + state.list_state.select(None); + state.page = std::cmp::max(state.page - 1, 1); + state.status = AsyncAnimeStateStatus::Empty; + true + }, + KeyCode::Right | KeyCode::Char('d') => { + state.list_state.select(None); + state.page = state.page + 1; + state.status = AsyncAnimeStateStatus::Empty; + true + }, + KeyCode::Enter => { + if let Some(u) = state.list_state.selected() && let Some(res) = state.res.clone() { + let items = utils::utils::hash_sort(&res); + if let Some(item) = items.get(u) { + if let Some(url) = res.get(*item) { + //source.episode_play_url(url); + let so = state.animesource.clone(); + let u = url.clone(); + tokio::spawn(async move { + if let Ok(vlc_url) = so.episode_play_url(&u).await { + if !vlc_url.is_empty() { + let _ = Command::new("vlc").arg(vlc_url) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .spawn(); + } + } + }); + } + } + } + true + } + _ => {false} + }, + _=>{false} + } + // match k { + // KeyCode::Up => { + // state.list_state.select_previous(); + // }, + // KeyCode::Down => { + // state.list_state.select_next(); + // } + // _ => {} + // } + }) + } + } + + pub fn block_library(&mut self, lib_type: Library::LibraryType) -> CurrentWidget { + // let render_ls = Rc::clone(list_state); + // let input_ls = Rc::clone(list_state); + let render_ls = Rc::clone(&self.library.library_list_state); + let input_ls = Rc::clone(&self.library.library_list_state); + let tx_clone_render = self.library.tx.clone(); + let tx_clone_input = self.library.tx.clone(); + CurrentWidget { + render: Box::new(move |frame: &mut Frame, area: Rect| { + let b = Block::new() + .borders(Borders::ALL) + .border_type(BorderType::Rounded) + .border_style(Style::new().fg(ratatui::style::Color::Cyan)); + let mut state = render_ls.borrow_mut(); + let l = Library::Library::new(lib_type,tx_clone_render.clone()); //TODO + + //frame.render_widget(b, area); + frame.render_stateful_widget(l, area, &mut *state); + }), handle_input: Box::new(move |k: KeyCode| { + let mut state = input_ls.borrow_mut(); + let tx_clone_input_clone = tx_clone_input.clone(); + match state.status { + LibraryStateStatus::Lib(p) => { + match k { + KeyCode::Left | KeyCode::Char('a') => { + state.list_state_source.select(None); + state.status = LibraryStateStatus::Lib(p-1); + true + } + KeyCode::Right | KeyCode::Char('d') => { + state.list_state_source.select(None); + state.status = LibraryStateStatus::Lib(p+1); + true + } + KeyCode::Up | KeyCode::Char('w') => { + if state.entry_selected { + state.list_state_eps.select_previous(); + } else { + state.list_state_source.select_previous(); + } + true + }, + KeyCode::Down | KeyCode::Char('s')=> { + if state.entry_selected { + state.list_state_eps.select_next(); + } else { + state.list_state_source.select_next(); + } + true + }, + KeyCode::Enter => { + if state.entry_selected { + + } else { + let state_bo = &state; + if let Some(sel) = state_bo.list_state_source.selected() && let Some(current) = state_bo.current_source.clone() { + let kind = match lib_type { + Library::LibraryType::Anime => SourceType::Anime, + Library::LibraryType::Manga => SourceType::Manga + }; + if let Some(u_list) = state_bo.lib.get(¤t) { + if let Some(u) = u_list.get(sel) { + let u_clone = u.clone(); + let lib_type_clone = lib_type.clone(); + tokio::spawn(async move { + if let Ok(ex) = Extension::buildfromName(kind, ¤t) { + let hs = match lib_type_clone { + Library::LibraryType::Anime => { + let s: Box = ex.toAnimeSource(); + s.lib_url_parse(u_clone.episodes).await + } + Library::LibraryType::Manga => { + let m: Box = ex.toMangaSource(); + m.lib_url_parse(u_clone.episodes).await + } + }; + if let Ok(o) = hs { + tx_clone_input_clone.send(o); + } + } + }); + } + } + } + } + true + } + _ => {false} + } + }, + _ => {false} + } + }) + } + } + // pub fn new_anime_list(&mut self) -> CurrentWidget { + // // let ls = Rc::new(RefCell::new(ListState::default())); + // // let render_ls = Rc::clone(&ls); + // // let input_ls = Rc::clone(&ls); + // CurrentWidget { render: Box::new(move |frame: &mut Frame, area: Rect| { + // // self.anime_list.render_asynclist(area, frame.buffer_mut()) + // }), handle_input: Box::new(move |k: KeyCode| {}) } + // } + // pub fn block_manga_library(&mut self) -> CurrentWidget { + // let ls = Rc::new(RefCell::new(ListState::default())); + // let render_ls = Rc::clone(&ls); + // let input_ls = Rc::clone(&ls); + // CurrentWidget { + // render: Box::new(move |frame: &mut Frame, area: Rect| { + // let b = Block::new() + // .borders(Borders::ALL) + // .border_type(BorderType::Rounded) + // .border_style(Style::new().fg(ratatui::style::Color::LightCyan)); + // // let fld = match read_manga_folder() { + // // Ok(a) => a, + // // _ => vec![] + // // }; + // // let items = fld.iter().enumerate().map(|(i, folder)| { + // // ListItem::new(folder.to_string_lossy().into_owned()) + // // }); + // // let l = List::new(items).block(b).highlight_style(Style::new().fg(tailwind::BLACK).bg(tailwind::AMBER.c200)); + // let mut state = render_ls.borrow_mut(); + // frame.render_stateful_widget(l, area, &mut *state); + // }), + // handle_input: Box::new(move |k: KeyCode| { + // let mut state = input_ls.borrow_mut(); + // match k { + // KeyCode::Up => { + // state.select_previous(); + // }, + // KeyCode::Down => { + // state.select_next(); + // } + // _ => {} + // } + // }) + // } + // } +} + diff --git a/src/mangatab.md b/src/mangatab.md new file mode 100644 index 0000000..719fa80 --- /dev/null +++ b/src/mangatab.md @@ -0,0 +1,29 @@ +use ratatui::{ + DefaultTerminal, buffer::Buffer, crossterm::event::{self, Event, KeyCode, KeyEventKind, KeyModifiers}, layout::{Constraint, Layout, Rect}, macros::horizontal, style::{Color, Stylize, palette::tailwind}, text::{Line, Text}, widgets::{Block, List, ListDirection, ListItem, Padding, Paragraph, Tabs, Widget} +}; +use strum::{Display, EnumIter, FromRepr, IntoEnumIterator}; + +#[derive(Default)] +pub struct Manga { + selected_tab: MangaTab +} + +#[derive(Default, Clone, Copy, Display, FromRepr, EnumIter)] +pub enum MangaTab { + #[default] + #[strum(to_string = "Latest")] + Tab1, + #[strum(to_string = "Top")] + Tab2, + #[strum(to_string = "Search")] + Tab3 +} + +impl MangaTab { + pub fn title(self) -> Line<'static> { + format!(" {self} ") + .fg(tailwind::SLATE.c200) + // .bg(self.palette().c900) + .into() + } +} \ No newline at end of file diff --git a/src/mmain.md b/src/mmain.md new file mode 100644 index 0000000..20c487a --- /dev/null +++ b/src/mmain.md @@ -0,0 +1,73 @@ +use color_eyre::Result; +use ratatui::{ + DefaultTerminal, buffer::Buffer, crossterm::event::{self, Event, KeyCode, KeyEventKind, KeyModifiers}, layout::{Constraint, Layout, Rect}, style::{Color, Stylize, palette::tailwind}, symbols, text::Line, widgets::{Block, List, Padding, Paragraph, Tabs, Widget} +}; +use strum::{Display, EnumIter, FromRepr, IntoEnumIterator}; + +fn main() -> Result<()> { + color_eyre::install()?; + let terminal = ratatui::init(); + let app_result = App::default().run(terminal); + ratatui::restore(); + app_result +} + +#[derive(Default)] +struct App { + state: AppState, +} + +#[derive(Default, Clone, Copy, PartialEq, Eq)] +enum AppState { + #[default] + Running, + Quitting, +} + + +impl App { + fn run(mut self, mut terminal: DefaultTerminal) -> Result<()> { + while self.state == AppState::Running { + terminal.draw(|frame| frame.render_widget(&self, frame.area()))?; + self.handle_events()?; + } + Ok(()) + } + + fn handle_events(&mut self) -> std::io::Result<()> { + if let Event::Key(key) = event::read()? { + if key.kind == KeyEventKind::Press { + if key.modifiers.contains(KeyModifiers::CONTROL) { + match key.code { + KeyCode::Char('c') => self.quit(), + _ => {} + } + } else { + match key.code { + KeyCode::Esc => self.quit(), + // KeyCode::Up => self.up, + // KeyCode + _ => {} + } + } + } + } + Ok(()) + } + + pub fn quit(&mut self) { + self.state = AppState::Quitting; + } +} + +impl Widget for &App { + fn render(self, area: Rect, buf: &mut Buffer) { + let li = List::new([ + "1. Anime", + "2. Manga", + ]) + .highlight_symbol(">>") + .render(area, buf); + } +} + diff --git a/src/source.rs b/src/source.rs new file mode 100644 index 0000000..9aa0791 --- /dev/null +++ b/src/source.rs @@ -0,0 +1,146 @@ +use std::collections::HashMap; + +use async_trait::async_trait; + +pub mod animeworld; +pub mod mangaworld; + +pub const AVAIBLEANIMESOURCE: [&str; 1] = ["animeworld"]; +#[derive(Clone)] +pub enum SourceType { + Anime, Manga +} +impl SourceType { + pub fn to_string(self) -> String { + match self { + SourceType::Anime => "anime".to_owned(), + SourceType::Manga => "manga".to_owned() + } + } +} +pub enum SourceLanguages { + It, Jp, Eng +} + +pub struct Series { + episodes: Vec, + title: String, + lang: SourceLanguages, + url: String, + source: S +} +pub struct Episode { + pub title: String, + pub index: usize, + pub section: usize, + pub url: String +} +pub struct EpisodeListItem { + title: String, + url: String +} +pub struct Comic { + episodes: Vec, + title: String, + lang: SourceLanguages, + url: String, + source: S +} +pub struct Chapter { + pub title: String, + pub index: usize, + pub section: usize, + pub url: String +} + + + +#[async_trait] +pub trait AnimeSource: Send + Sync { + /*let mut items = hostname.split(".").collect::>(); + if let Some(f) = items.first() { + if *f != "www" { + items.insert(0, "www"); + } + } + Self { + url: items.join("."), + host: items[1..items.len() - 1].join("."), + hostname: "https://".to_owned()+&items[1..].join("."), + sourcetype + } */ + fn new(hostname: String, url: String) -> Self where Self: Sized; + + fn get_hostname(&self)->String; + fn get_url(&self)->String; + + /** { + let p = std::cmp::max(1, page); + let u = format!("{}/updated?page={}",self.url,p); + let a = get(u).await?.error_for_status()?.text().await?; + return Html::parse_document(&a); + }*/ + async fn fetch_updated_page(&self, page: usize)-> Result>; + fn extract_episode_from_updated(&self, document: String) -> Result, Box>; + + async fn fetch_search_page(&self, query: String, page: usize) -> Result>; + fn extract_episode_from_search(&self, document: String) -> Result, Box>; + + async fn listupdated(&self, page: usize) -> Result, Box> { + let body = self.fetch_updated_page(page).await?; + self.extract_episode_from_updated(body) + } + async fn search(&self, query: String, page: usize) -> Result, Box>{ + let body = self.fetch_search_page(query, page).await?; + self.extract_episode_from_search(body) + } + + async fn episode_openable_link(&self, card: Episode) -> Result>; + async fn episode_play_url(&self, url: &str) -> Result>; + async fn lib_url_parse(&self, url: String) -> Result, Box>; +} + +#[async_trait] +pub trait MangaSource: Send + Sync { + /*let mut items = hostname.split(".").collect::>(); + if let Some(f) = items.first() { + if *f != "www" { + items.insert(0, "www"); + } + } + Self { + url: items.join("."), + host: items[1..items.len() - 1].join("."), + hostname: "https://".to_owned()+&items[1..].join("."), + sourcetype + } */ + fn new(hostname: String, url: String) -> Self where Self: Sized; + + fn get_hostname(&self)->String; + fn get_url(&self)->String; + + /** { + let p = std::cmp::max(1, page); + let u = format!("{}/updated?page={}",self.url,p); + let a = get(u).await?.error_for_status()?.text().await?; + return Html::parse_document(&a); + }*/ + async fn fetch_updated_page(&self, page: usize)-> Result>; + fn extract_episode_from_updated(&self, document: String) -> Result, Box>; + + async fn fetch_search_page(&self, query: String, page: usize) -> Result>; + fn extract_episode_from_search(&self, document: String) -> Result, Box>; + + async fn listupdated(&self, page: usize) -> Result, Box> { + let body = self.fetch_updated_page(page).await?; + self.extract_episode_from_updated(body) + } + async fn search(&self, query: String, page: usize) -> Result, Box>{ + let body = self.fetch_search_page(query, page).await?; + self.extract_episode_from_search(body) + } + async fn lib_url_parse(&self, url: String) -> Result, Box>; + // async fn chapter(&self, card: Episode) -> Result>; + // async fn episode_play_url(&self, url: &str) -> Result>; + +} diff --git a/src/source/animeworld.rs b/src/source/animeworld.rs new file mode 100644 index 0000000..b89533f --- /dev/null +++ b/src/source/animeworld.rs @@ -0,0 +1,139 @@ + +use std::{collections::HashMap, process::Command}; + +use reqwest::get; +use scraper::{Html, Selector}; + +use crate::source::{AnimeSource, Episode, EpisodeListItem, SourceType}; + +pub struct Animeworld { + url: String, + hostname:String, + sourcetype: SourceType +} + +impl Animeworld { + fn id_extract( s:&str) -> Option<&str> { + s.split("/").last() + } +} +#[async_trait::async_trait] +impl AnimeSource for Animeworld { + fn get_hostname(&self)->String { + self.hostname.clone() + } + fn get_url(&self) -> String { + self.url.clone() + } + + fn new(hostname: String, url: String) -> Self { + Self { + url, + hostname, + sourcetype: crate::source::SourceType::Anime + } + } + async fn listupdated(&self, page: usize) -> Result, Box> { + let body = self.fetch_updated_page(page).await?; + self.extract_episode_from_updated(body) + } + fn extract_episode_from_search(&self, body: String) -> Result, Box> { + // let sel = Selector::parse("div.film-list").unwrap(); + let document = Html::parse_document(&body); + let sel_inner = Selector::parse("div.inner").unwrap(); + let sel_episode_number = Selector::parse("div.ep").unwrap(); + let sel_anime_name = Selector::parse("a.name").unwrap(); + let cards = document.select(&sel_inner) + .filter_map(|card| { + let carde = card.select(&sel_episode_number).next()?; + let index = carde.inner_html().trim().split(" ").last().unwrap().parse::().unwrap_or(0); + // log!("index: {}", index); + let carda = card.select(&sel_anime_name).next()?; + + // log!("Card a found"); + let url = carda.value().attr("href").unwrap_or("").trim().to_owned(); + // log!("Card url: {}",url); + let title = carda.inner_html().trim().to_owned(); + // log!("Card title: {}", title); + Some(Episode { + title, + index, + url, + section: 0 + }) + }); + Ok(cards.collect::>()) + } + fn extract_episode_from_updated(&self, body: String) -> Result, Box> { + self.extract_episode_from_search(body) + } + + + async fn episode_openable_link(&self, card: Episode) -> Result> { + let id= Self::id_extract(&card.url).unwrap(); + let u = format!("{}/api/episode/serverPlayerAnimeWorld?id={}", self.url, id); + let r = get(u).await?.error_for_status()?.text().await?; + let b = Html::parse_document(&r); + let select_source = Selector::parse("source").unwrap(); + Ok(b.select(&select_source).next().unwrap().value().attr("src").unwrap_or("").to_owned()) + } + async fn episode_play_url(&self, url: &str) -> Result> { + if let Some(id) = Self::id_extract(url) { + let u = format!("{}/api/episode/serverPlayerAnimeWorld?id={}", self.url, id); + match Command::new("curl") + .arg("-L") + .arg(u).output() { + Ok(output) => { + // log!("{}"); + if let Ok(html) = String::from_utf8(output.stdout) { + let b = Html::parse_document(&html); + let select_source = Selector::parse("source").unwrap(); + if let Some(c) = b.select(&select_source).next() { + return Ok(c.attr("src").unwrap_or("").to_owned()); + } + } + } + _ => {} + } + } + Ok(String::new()) + + } + async fn fetch_search_page(&self, query: String, page: usize) -> Result> { + // if page < 1 { page = 1}; + let u = format!("{}?keyword={}&page={}",self.get_url(), query, page); + let r = reqwest::get(u).await?.error_for_status()?.text().await?; + return Ok(r); + } + + async fn search(&self, query: String, page: usize) -> Result, Box>{ + let body = self.fetch_search_page(query, page).await?; + self.extract_episode_from_search(body) + } + + async fn fetch_updated_page(&self, page: usize)-> Result> { + let p = std::cmp::max(1, page); + let u = format!("{}/updated?page={}",self.get_url(),p); + let a = get(u).await?.error_for_status()?.text().await?; + return Ok(a); + } + + async fn lib_url_parse(&self, url: String) -> Result, Box>{ + let mut hs = HashMap::::new(); + if let Ok(a) = get(url).await?.error_for_status()?.text().await { + let b = Html::parse_document(&a); + let select_episodes = Selector::parse("li.episode a").unwrap(); + for ep in b.select(&select_episodes) { + let alink = ep.value(); + hs.insert(ep.inner_html(), EpisodeListItem { + title: format!("Ep. {:>4}",ep.inner_html()), + url: alink.attr("href").unwrap_or("").trim().to_owned() + }); + } + } + return Ok(hs); + } + // async fn fetch_html(url: String) -> Result { + // get(url).await?.error_for_status()?.text().await + // } +} \ No newline at end of file diff --git a/src/source/mangaworld.rs b/src/source/mangaworld.rs new file mode 100644 index 0000000..e2e0b0c --- /dev/null +++ b/src/source/mangaworld.rs @@ -0,0 +1,179 @@ +use std::collections::HashMap; + +use reqwest::get; +use scraper::{Html, Selector}; +use tokio::io::split; + +use crate::source::{Chapter, EpisodeListItem, MangaSource, SourceType}; + +pub struct Mangaworld { + url: String, + hostname:String, + sourcetype: SourceType +} + +#[async_trait::async_trait] +impl MangaSource for Mangaworld { + fn get_hostname(&self)->String { + self.hostname.clone() + } + fn get_url(&self) -> String { + self.url.clone() + } + + fn new(hostname: String, url: String) -> Self { + Self { + url, + hostname, + sourcetype: crate::source::SourceType::Manga + } + } + async fn listupdated(&self, page: usize) -> Result, Box> { + let body = self.fetch_updated_page(page).await?; + self.extract_episode_from_updated(body) + } + //https://www.mangaworld.mx/archive?keyword= + fn extract_episode_from_search(&self, body: String) -> Result, Box> { + let document = Html::parse_document(&body); + let sel_inner = Selector::parse("div.entry").unwrap(); + // let sel_episode_number = Selector::parse("a.xanh").unwrap(); + let sel_anime_name = Selector::parse("a.manga-title").unwrap(); + let cards = document.select(&sel_inner) + .filter_map(|card| { + // let carde = card.select(&sel_episode_number).next()?; + // let index = carde.inner_html().trim().split(" ").last().unwrap().parse::().unwrap_or(0); + // log!("index: {}", index); + let carda = card.select(&sel_anime_name).next()?; + + // log!("Card a found"); + let url = carda.value().attr("href").unwrap_or("").trim().to_owned(); + // log!("Card url: {}",url); + let title = carda.inner_html().trim().to_owned(); + // log!("Card title: {}", title); + Some(Chapter { + title, + index: 0, + url, + section: 0 + }) + }); + Ok(cards.collect::>()) + } + //https://www.mangaworld.ac/ + fn extract_episode_from_updated(&self, body: String) -> Result, Box> { + let document = Html::parse_document(&body); + let sel_inner = Selector::parse("div.entry").unwrap(); + let sel_episode_number = Selector::parse("a.xanh").unwrap(); + let sel_anime_name = Selector::parse("a.manga-title").unwrap(); + let cards = document.select(&sel_inner) + .filter_map(|card| { + let carde = card.select(&sel_episode_number).next()?; + let index = carde.inner_html().trim().split(" ").last().unwrap().parse::().unwrap_or(0); + // log!("index: {}", index); + let carda = card.select(&sel_anime_name).next()?; + + // log!("Card a found"); + let url = carda.value().attr("href").unwrap_or("").trim().to_owned(); + // log!("Card url: {}",url); + let title = carda.inner_html().trim().to_owned(); + // log!("Card title: {}", title); + Some(Chapter { + title, + index, + url, + section: 0 + }) + }); + Ok(cards.collect::>()) + } + // async fn episode_openable_link(&self, card: Chapter) -> Result> { + // let id= Self::id_extract(&card.url).unwrap(); + // let u = format!("{}/api/episode/serverPlayerAnimeWorld?id={}", self.url, id); + // let r = get(u).await?.error_for_status()?.text().await?; + // let b = Html::parse_document(&r); + // let select_source = Selector::parse("source").unwrap(); + // Ok(b.select(&select_source).next().unwrap().value().attr("src").unwrap_or("").to_owned()) + // } + async fn fetch_search_page(&self, query: String, page: usize) -> Result> { + // if page < 1 { page = 1}; + let u = format!("{}?keyword={}&page={}",self.get_url(), query, page); + let r = reqwest::get(u).await?.error_for_status()?.text().await?; + return Ok(r); + } + + // async fn search(&self, query: String, page: usize) -> Result, Box>{ + // let body = self.fetch_search_page(query, page).await?; + // self.extract_episode_from_search(body) + // } + + async fn fetch_updated_page(&self, page: usize)-> Result> { + let p = std::cmp::max(1, page); + let u = format!("{}",self.get_url()); + let a = get(u).await?.error_for_status()?.text().await?; + return Ok(a); + } + async fn lib_url_parse(&self, url: String) -> Result, Box> { + let mut hs = HashMap::::new(); + if let Ok(a) = get(url).await?.error_for_status()?.text().await { + let b = Html::parse_document(&a); + // let select_chapters_wrapper = Selector::parse("").unwrap(); + let select_volume_element = Selector::parse(".chapters-wrapper .volume-element").unwrap(); + let select_volume_name = Selector::parse(".volume .volume-name").unwrap(); + let select_episodes = Selector::parse("div.chapter a.chap").unwrap(); + let select_span = Selector::parse("span").unwrap(); + // for ep in b.select(&select_episodes) { + // // let alink = ep.value(); + // if let Some(span) = ep.select(&select_span).next() { + // hs.insert(span.inner_html(), EpisodeListItem { + // title: format!("Volume {:>4} Capitolo {:>4}",,span.inner_html()), + // url: alink.attr("href").unwrap_or("").trim().to_owned() + // }); + // } + // } + for vol in b.select(&select_volume_element) { + if let Some(volname) = vol.select(&select_volume_name).next() { + let volume = volname.text().next(); + for ep in vol.select(&select_episodes) { + if let Some(span) = ep.select(&select_span).next() { + let alink = ep.value(); + let chapter = span.inner_html(); + let x = volume + .and_then(|s| s.split_whitespace().nth(1)) + .and_then(|second| second.split_once('.')); + let y = chapter.split_whitespace().nth(1) + .and_then(|second| second.split_once('.')); + + let title_vol = match x { + Some((vola,volb)) => { + if volb.is_empty() { + format!("Volume {:>4}", vola) + } else { + format!("Volume {:>4}.{}", vola,volb) + } + }, + _ => "".to_owned() + }; + let title_cha = match y { + Some((cha,chb)) => { + if chb.is_empty() { + format!("Capitolo {:>4}", cha) + } else { + format!("Capitolo {:>4}.{}", cha,chb) + } + }, + _ => "".to_owned() + }; + let title_sep = if !title_vol.is_empty() && !title_cha.is_empty() {" "} else {""}; + hs.insert(span.inner_html(), EpisodeListItem { + title: format!("{}{}{}", title_vol,title_sep, title_cha), + url: alink.attr("href").unwrap_or("").trim().to_owned() + }); + } + } + } + } + } + return Ok(hs); + } + +} diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..73f893b --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,39 @@ +pub mod utils { + use std::collections::HashMap; + + #[macro_export] + macro_rules! log { + ($($arg:tt)*) => {{ + let file_result = std::fs::OpenOptions::new() + .create(true) + .append(true) + .open("./debug.log"); + + if let Ok(mut file) = file_result { + use std::io::Write; + let _ = writeln!(file, $($arg)*); + } + }}; + } + + pub fn first_capital(s: &str) -> String { + let mut chars = s.chars(); + match chars.next() { + None => String::new(), + Some(first) => first.to_uppercase().collect::() + chars.as_str(), + } + } + + pub fn hash_sort(res: &HashMap) -> Vec<&String> { + let mut items = res.keys().collect::>(); + items.sort_by(|a,b|{ + let aname = a.splitn(2, "di ").nth(1).unwrap_or(""); + let bname = b.splitn(2, "di ").nth(1).unwrap_or(""); + match aname.cmp(bname) { + std::cmp::Ordering::Equal => a.cmp(b), + o => o + } + }); + items + } +}