diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..be8dc5c --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1427 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" + +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "console" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "unicode-width", + "windows-sys 0.52.0", +] + +[[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-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "crossterm" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" +dependencies = [ + "bitflags 2.4.2", + "crossterm_winapi", + "libc", + "mio", + "parking_lot", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" +dependencies = [ + "winapi", +] + +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + +[[package]] +name = "encoding_rs" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "h2" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + +[[package]] +name = "hermit-abi" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d3d0e0f38255e7fa3cf31335b3a56f05febd18025f4db5ef7a0cfb4f8da651f" + +[[package]] +name = "hostname" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +dependencies = [ + "libc", + "match_cfg", + "winapi", +] + +[[package]] +name = "http" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "0.14.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "indicatif" +version = "0.17.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb28741c9db9a713d93deb3bb9515c20788cef5815265bee4980e87bde7e0f25" +dependencies = [ + "console", + "instant", + "number_prefix", + "portable-atomic", + "unicode-width", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + +[[package]] +name = "is-url" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc3c11df614d56dcf271409b7a7efbcec8c07f55f685651232ddf26054d166b" +dependencies = [ + "lazy_static", + "regex", +] + +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "js-sys" +version = "0.3.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" + +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" + +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "match_cfg" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" + +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "number_prefix" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" + +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "openssl" +version = "0.10.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15c9d69dd87a29568d4d017cfe8ec518706046a05184e5aea92d0af890b803c8" +dependencies = [ + "bitflags 2.4.2", + "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", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e1bf214306098e4832460f797824c05d25aacdf896f64a985fb0fd992454ae" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.48.5", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[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.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2900ede94e305130c13ddd391e0ab7cbaeb783945ae07a279c268cb05109c6cb" + +[[package]] +name = "portable-atomic" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" + +[[package]] +name = "proc-macro2" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "regex" +version = "1.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b7fa1134405e2ec9353fd416b17f8dacd46c473d7d3fd1cf202706a14eb792a" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "reqwest" +version = "0.11.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "system-configuration", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustix" +version = "0.38.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" +dependencies = [ + "bitflags 2.4.2", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "ryu" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" + +[[package]] +name = "schannel" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "security-framework" +version = "2.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.195" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.195" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "signal-hook" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" +dependencies = [ + "libc", + "mio", + "signal-hook", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" + +[[package]] +name = "socket2" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "syn" +version = "2.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tempfile" +version = "3.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" +dependencies = [ + "cfg-if", + "fastrand", + "redox_syscall", + "rustix", + "windows-sys 0.52.0", +] + +[[package]] +name = "terminal_download" +version = "0.1.0" +dependencies = [ + "bytes", + "crossterm", + "futures", + "hostname", + "indicatif", + "is-url", + "reqwest", + "tokio", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +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.35.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-width" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" + +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[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.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bde2032aeb86bdfaecc8b261eef3cba735cc426c1f3a3416d1e0791be95fc461" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" + +[[package]] +name = "web-sys" +version = "0.3.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58cd2333b6e0be7a39605f0e255892fd7418a682d8da8fe042fe25128794d2ed" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[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-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[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.0", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..9a173f7 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "terminal_download" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +bytes = "1.5.0" +crossterm = "0.27.0" +futures = "0.3.30" +hostname = "0.3.1" +indicatif = "0.17.7" +is-url = "1.0.4" +reqwest = {version = "0.11.23", features = ["blocking"]} +tokio = {version = "1.35.1", features = ["full"]} \ No newline at end of file diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..52c2d85 --- /dev/null +++ b/build.sh @@ -0,0 +1,2 @@ +cargo build --target x86_64-pc-windows-gnu --release +cargo build --target x86_64-unknown-linux-gnu --release diff --git a/src/download/mod.rs b/src/download/mod.rs new file mode 100644 index 0000000..0c75474 --- /dev/null +++ b/src/download/mod.rs @@ -0,0 +1,130 @@ +use std::io::prelude::*; +use std::fs::File; +use std::io::SeekFrom; +use std::sync::Arc; +use std::thread; + +mod network; +mod progress; + +static ONE_KB: u64 = 1024; + +pub struct Download { + pub url: String, + pub filename: String, + pub memory: u64, + pub threads: u64, + pub network: network::Network, + pub progress: progress::Progress, +} + +impl Default for Download { + fn default() -> Download { + Download { + url: "".to_string(), + filename: "".to_string(), + memory: 256, + threads: 1, + network: network::Network { + ..Default::default() + }, + progress: progress::Progress { + ..Default::default() + }, + } + } +} + +impl Download { + pub fn get(self) { + let content_length_resp: Option = self.network.get_content_length(&self.url); + + match content_length_resp { + Some(content_length) => { + let children = Download::spawn_threads(self, content_length); + for child in children { + let _ = child.join(); + } + } + None => println!("Content length is not present for this URL. Support for this type of hosted file will be added in the future."), + } + + } + + fn calculate_ranges( + threads: u64, + content_length: u64, + max_buffer_size: u64, + mut progress: progress::Progress + ) -> (progress::Progress, Vec<(String, u64, u64, u64, u64)>) { + let mut range_start = 0; + let mut ranges = vec![]; + let chunk_size = content_length / threads - 1; + + println!("Processing ranges: "); + for thread in 0..threads { + let mut range_end = chunk_size + range_start; + if thread == (threads - 1) { + range_end = content_length + } + + let thread_number = thread + 1; + let range: String = format!("bytes={}-{}", range_start, range_end); + let range_to_process: u64 = range_end - range_start; + let buffer_chunks: u64 = range_to_process / max_buffer_size; + let chunk_remainder: u64 = range_to_process % max_buffer_size; + + println!(" Thread: {}, range: {}, chunks: {}, chunk_remainder: {}", thread_number, range, buffer_chunks, chunk_remainder); + ranges.push((range, range_start, thread_number, buffer_chunks, chunk_remainder)); + progress.add(range_to_process, &thread_number); + + range_start = range_start + chunk_size + 1; + } + return (progress, ranges); + } + + fn spawn_threads(self, content_length: u64) -> Vec> { + let mut children = vec![]; + let max_buffer_size = ONE_KB * self.memory; + let (progress, ranges) = Download::calculate_ranges(self.threads, content_length, max_buffer_size, self.progress); + + let progress_arc = Arc::new(progress); + let network_arc = Arc::new(self.network); + + println!("Spawning Threads..."); + for (range, range_start, thread_number, buffer_chunks, chunk_remainder) in ranges { + + let network_ref = network_arc.clone(); + let progress_ref = progress_arc.clone(); + let filename_ref = self.filename.clone(); + let url_ref = self.url.clone(); + + children.push( + thread::spawn(move || { + + let mut file_handle = File::create(filename_ref).unwrap(); + file_handle.seek(SeekFrom::Start(range_start)).unwrap(); + + let mut file_range_resp = network_ref.make_request(&url_ref, range); + + for _x in 0..buffer_chunks { + let mut vector_buffer = vec![0u8; max_buffer_size as usize]; + let file_range_ref = file_range_resp.by_ref(); + file_range_ref.read_exact(&mut vector_buffer).unwrap(); + file_handle.write(&vector_buffer).unwrap(); + file_handle.flush().unwrap(); + progress_ref.inc(max_buffer_size, &thread_number); + } + + if chunk_remainder != 0 { + file_range_resp.copy_to(&mut file_handle).unwrap(); + progress_ref.set_position(chunk_remainder, &thread_number); + } + progress_ref.finish(&thread_number); + }) + ); + } + progress_arc.clone().join_and_clear(); + return children; + } +} diff --git a/src/download/network.rs b/src/download/network.rs new file mode 100644 index 0000000..79dd056 --- /dev/null +++ b/src/download/network.rs @@ -0,0 +1,31 @@ +extern crate reqwest; + +use self::reqwest::header; +use self::reqwest::Client; +use self::reqwest::Response; + +pub struct Network { + pub client: Client, +} + +impl Default for Network { + fn default() -> Network { + Network { + client: Client::new(), + } + } +} + +impl Network { + pub fn make_request(&self, url: &String, range: String) -> Response { + let request = self.client + .get(url) + .header(header::RANGE, range); + + return request.send().expect("Could not send request."); + } + + pub fn get_content_length(&self, url: &String) -> Option { + return self.make_request(url, "".to_string()).content_length(); + } +} diff --git a/src/download/progress.rs b/src/download/progress.rs new file mode 100644 index 0000000..996d25e --- /dev/null +++ b/src/download/progress.rs @@ -0,0 +1,62 @@ +extern crate indicatif; + +use self::indicatif::{MultiProgress, ProgressBar, ProgressStyle}; + +use std::collections::HashMap; + +pub struct Progress { + pub multi_progress: MultiProgress, + pub progress_bars: HashMap, +} + +impl Default for Progress { + fn default() -> Progress { + Progress { + multi_progress: MultiProgress::new(), + progress_bars: HashMap::new(), + } + } +} + +impl Progress { + pub fn add(&mut self, range: u64, thread_number: &u64) { + let pb = self.multi_progress.add(ProgressBar::new(range)); + let style: ProgressStyle = ProgressStyle::default_bar() + .template("[{bar:40.cyan/blue}] {bytes}/{total_bytes} ({bytes_per_sec}, {eta} remaining) {msg}") + .progress_chars("##-"); + pb.set_style(style); + pb.set_message(&format!("thread #{}", &thread_number)); + self.progress_bars.insert(thread_number.to_owned(), pb); + } + + pub fn inc(&self, amount: u64, thread_number: &u64) { + let pb = match self.progress_bars.get(thread_number) { + Some(x) => x, + None => return, + }; + pb.inc(amount); + } + + pub fn set_position(&self, amount: u64, thread_number: &u64) { + let pb = match self.progress_bars.get(thread_number) { + Some(x) => x, + None => return, + }; + pb.set_position(amount); + } + + pub fn finish(&self, thread_number: &u64) { + let pb = match self.progress_bars.get(thread_number) { + Some(x) => x, + None => return, + }; + pb.finish_with_message("--done--"); + } + + pub fn join_and_clear(&self) { + self.multi_progress.join_and_clear().unwrap(); + } +} + + + diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..a372a23 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,633 @@ +#![warn(dead_code)] +#![allow(unused_braces)] +#![warn(unused)] + +use std::collections::HashMap; +use std::io::{self, Write, Stdout}; +// use std::fs::{self, File, OpenOptions}; +use std::{thread}; +use std::sync::{Arc, Mutex}; +use std::io::{stdout}; +use std::time::Duration; + +use tokio::{self, + task::JoinHandle, + io::{AsyncWriteExt, AsyncReadExt}, + fs::File +}; + +use reqwest; +use is_url::is_url; + +use indicatif::{ProgressBar, MultiProgress, ProgressStyle}; + +use crossterm::terminal::{self, Clear, ClearType}; +use crossterm::cursor::{MoveTo}; +use crossterm::event::{poll, read, Event, KeyCode, KeyModifiers}; +use crossterm::{QueueableCommand, ExecutableCommand}; + +#[derive(Default)] +struct Prompt { + buffer: Vec, + cursor: usize, +} + +impl Prompt { + fn sync_terminal_cursor(&mut self, qc: &mut impl Write, x: usize, y: usize, w: usize) -> io::Result<()> { + if let Some(_) = w.checked_sub(2) { + let _ = qc.queue(MoveTo(x as u16 + self.cursor as u16, y as u16))?; + } + Ok(()) + } + + fn insert(&mut self, x: char) { + if self.cursor > self.buffer.len() { + self.cursor = self.buffer.len() + } + self.buffer.insert(self.cursor, x); + self.cursor += 1; + } + + fn insert_str(&mut self, text: &str) { + for x in text.chars() { + self.insert(x) + } + } + + fn left_char(&mut self) { + if self.cursor > 0 { + self.cursor -= 1; + } + } + + fn right_char(&mut self) { + if self.cursor < self.buffer.len() { + self.cursor += 1; + } + } + + fn at_cursor(&self) -> char { + self.buffer.get(self.cursor).cloned().unwrap_or('\n') + } + + fn left_word(&mut self) { + while self.cursor > 0 && self.at_cursor().is_whitespace() { + self.cursor -= 1; + } + while self.cursor > 0 && !self.at_cursor().is_whitespace() { + self.cursor -= 1; + } + } + + fn right_word(&mut self) { + while self.cursor < self.buffer.len() && self.at_cursor().is_whitespace() { + self.cursor += 1; + } + while self.cursor < self.buffer.len() && !self.at_cursor().is_whitespace() { + self.cursor += 1; + } + } + + fn backspace(&mut self) { + if self.cursor > 0 { + self.cursor -= 1; + self.buffer.remove(self.cursor); + } + } + + fn _before_cursor(&self) -> &[char] { + &self.buffer[..self.cursor] + } + + fn _after_cursor(&self) -> &[char] { + &self.buffer[self.cursor..] + } + + fn clear(&mut self) { + self.buffer.clear(); + self.cursor = 0; + } + + fn _delete_until_end(&mut self) { + while self.cursor < self.buffer.len() { + self.buffer.pop(); + } + } +} + +#[derive(Default)] +struct DownloadResults { + dr: Vec +} + +impl DownloadResults { + fn push(&mut self, string: String) { + self.dr.push(string); + } + + fn render(&mut self, height_bound: usize, stdout: &mut Stdout) { + let n = self.dr.len(); + let m = n.checked_sub(height_bound).unwrap_or(0); + for (index, dr) in self.dr.iter().skip(m).enumerate() { + stdout.queue(MoveTo(0, index as u16)).unwrap(); + stdout.write(dr.as_bytes()).unwrap(); + } + } +} + +fn slice_from_end(s: &str, n: usize) -> Option<&str> { + s.char_indices().rev().nth(n).map(|(i, _)| &s[i..]) +} + +fn slice_from_start(s: String, n: usize) -> String { + s.chars().into_iter().take(n).collect() +} + +fn main() -> io::Result<()> { + let mut stdout = stdout(); + + let mut prompt = Prompt::default(); + + let _ = terminal::enable_raw_mode().unwrap(); + + let (mut w, mut h) = terminal::size().unwrap(); + let bar_char = "─"; + let mut bar = bar_char.repeat(w as usize); + + let mut download_requested: Vec = Vec::new(); + let mut download_results = DownloadResults::default(); + + let mut quit = false; + + let task_count = Arc::new(Mutex::new(0)); + + while !quit { + while poll(Duration::ZERO)? { + match read()? { + Event::Resize(nw, nh) => { + w = nw; + h = nh; + bar = bar_char.repeat(w as usize / bar_char.len()); + }, + Event::Paste(data) => prompt.insert_str(&data), + Event::Key(event) => { + let key = event.code; + + match key { + KeyCode::Esc => {quit = true}, + KeyCode::Backspace => prompt.backspace(), + KeyCode::Char(x) => { + if event.modifiers.contains(KeyModifiers::CONTROL) { + match x { + 'c' => quit = true, + 'k' => prompt.clear(), + _ => {} + } + } else { + // url.push(x); + prompt.insert(x); + } + }, + KeyCode::Enter => { + let temp_url = String::from_iter(&prompt.buffer); + let this_url = temp_url.clone(); + + download_requested.push(temp_url); + let task_count = Arc::clone(&task_count); + + prompt.clear(); + + let _ = terminal::disable_raw_mode().unwrap(); + // thread::scope(|s| { + // s.spawn(|| { + // dbg!("thread started"); + // let rt = runtime::Runtime::new().unwrap(); + + let display_name = format!("{}...", slice_from_start(this_url.to_string(), &this_url.len() - 15)); + + // download_file_in_pieces( + // download_requested.get(download_requested.len() - 1).unwrap().as_str(), + // format!("output{}", download_requested.len()).as_str(), + // task_count + // ); + + + // let url = this_url.clone(); + // let db = download_requested.len().clone(); + + // download_file_in_pieces( + // &url, + // format!("output{}", db).as_str(), + // task_count + // ) + + stdout.execute(Clear(ClearType::All)).unwrap(); + + thread::scope(|s| { + s.spawn(|| { + let download_try = download_file_in_pieces( + &this_url, + task_count + ); + if let Ok(_) = download_try { + // println!("download thread finished"); + download_results.push(format!("{} finished", display_name)); + // terminal::enable_raw_mode().unwrap(); + // rt.shutdown_background() + } else if let Err(reason) = download_try { + // eprintln!("an error occurred on the download thread."); + download_results.push(format!("{} failed. {}", display_name, reason)); + // rt.shutdown_background() + } + + let _ = terminal::enable_raw_mode().unwrap(); + thread::sleep(Duration::from_secs(5)); + }); + }); + + // println!("current concurrent download thread name={}", + // current_download.thread().name().unwrap_or("no_name")); + // while !current_download.is_finished() {}; + + // download_results.push(String::from(format!("finished {}", display_name))); + + // terminal::enable_raw_mode().unwrap(); + // }); + // }); + }, + KeyCode::Right => { + if event.modifiers.contains(KeyModifiers::CONTROL) { + prompt.right_word(); + } else { + prompt.right_char(); + } + }, + KeyCode::Left => { + if event.modifiers.contains(KeyModifiers::SHIFT) { + prompt.left_word(); + } else { + prompt.left_char(); + } + }, + _ => {} + } + }, + _ => {} + } + } + + stdout.queue(Clear(ClearType::All)).unwrap(); + download_results.render(1, &mut stdout); + + stdout.queue(MoveTo(0, h-3)).unwrap(); + stdout.write(b"Type in the URL, and hit enter to start the download! ESC to exit and Ctrl+K to clear the prompt").unwrap(); + + stdout.queue(MoveTo(0, h-2)).unwrap(); + stdout.write(bar.as_bytes()).unwrap(); + + stdout.queue(MoveTo(0, h-1)).unwrap(); + stdout.write(String::from_iter(&prompt.buffer).as_bytes()).unwrap(); + + // stdout.queue(MoveTo(terminal_cursor.x, terminal_cursor.y)).unwrap(); + if let Some(y) = h.checked_sub(1) { + let x = 0; + if let Some(w) = w.checked_sub(1) { + prompt.sync_terminal_cursor(&mut stdout, x, y as usize, w as usize)?; + } + } + + stdout.flush().unwrap(); + thread::sleep(Duration::from_millis(33)); + }; + + let _ = terminal::disable_raw_mode().unwrap(); + Ok(()) +} + +async fn download_chunk( + client: reqwest::Client, + url: String, + start: usize, + end: usize, + // file: Arc>, + filename: String, + pb: ProgressBar, + task_count: Arc> +) -> () { + let response = client + .get(&url) + .header("Range", format!("bytes={}-{}", start, end)) + .send() + .await; + + match response { + Ok(mut response) => { + // dbg!(start, end); + + // pb.set_position(0 as u64); + // let mut content = response.bytes().await.unwrap(); + // let mut content = Bytes::new().chunks(end - start); + + // println!("mutex lock for file unwraped on thread start={}", start); + // let mut file = file.lock().await; + // let mut content: Vec<&[u8]> = vec!(&Bytes::new()); + + let mut file = File::create(filename).await.unwrap(); + + let mut progress: u64 = 0; + pb.set_position(progress); + + while let Some(b) = response.chunk().await.unwrap() { + // let mut file = file.lock().await; + // let progress = (end - start) as u64 / b.len() as u64; + + progress += b.len() as u64; + + // println!("writing on thr start={} (chunk.length={}, progress={} of {})", + // start, b.len(), progress, (end - start)); + + file.write(&b).await.unwrap(); + file.flush().await.unwrap(); + // drop(file); + pb.set_position(progress); + }; + + // file.write_all(&mut content).await.unwrap(); + + // println!("mutex lock for file dropped on thread start={}", start); + + drop(file); + let mut count = task_count.lock().unwrap(); + *count -= 1; + + drop(count); + + pb.finish_and_clear(); + + // while response_chunk.is_some() { + // // println!("Chunk: {:?}", chunk); + // file.write_all(response_chunk.unwrap().chunks()).await; + // pb.set_position(chunk); + // } + + // pb.set_position(end as u64); + return; + } + + Err(err) => { + eprintln!("Error downloading chunk: {:?}", err); + return; + } + } + + // let mut count = task_count.lock().unwrap(); + // *count -= 1; + + // drop(count); + // pb.finish(); + +} + +fn parse_filename_from_url(url_string: String) -> String { + let url_vector = url_string.split('/').collect::>(); + let output_final_name = if url_string.chars().last().unwrap() == '/' { + url_vector.len().checked_sub(2).map(|i| url_vector[i]).unwrap() + } else { + url_string.split('/').collect::>().last().copied().unwrap() + }; + return String::from(output_final_name) +} + +#[tokio::main] +async fn download_file_in_pieces(url: &str, task_count: Arc>) + -> Result<(), String> { + if !is_url(url) { + return Err(format!("Invalid URL.")); + } + + let client: reqwest::Client = reqwest::Client::new(); + + let response_future = client + .get(url) + .send() + .await; + + if let Err(e) = response_future { + return Err(format!("Connection refused.")) + }; + + let response = response_future.unwrap(); + + let total_size: u64 = response.content_length().unwrap_or(0); + + // let chunk_size: usize = (total_size / 3) as usize; + let chunk_size: usize = if total_size > 1024 * 1024 * 40 { + if total_size >= 1024 * 1024 * 1024 { + (total_size / 6) as usize + } else { + (total_size / 3) as usize + } + } else { + total_size as usize + }; + + // dbg!(total_size); + + // let file = Arc::new( + // tokio::sync::Mutex::new( + // File::create(output) + // .await + // .expect("Error creating output file.") + // ) + // ); + + let mpb = MultiProgress::new(); + + // let pb = ProgressBar::new(total_size); + // pb.set_style(ProgressStyle::default_bar() + // .template("{msg}\n{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {bytes}/{total_bytes} ({bytes_per_sec}, {eta})")? + // .progress_chars("#>-")); + + let mut tasks: HashMap<(u64, usize), JoinHandle<()>> = HashMap::new(); + let mut pieces: HashMap = HashMap::new(); + + let mut count = task_count.lock().unwrap(); + *count += ((total_size + (chunk_size as u64) - 1) / chunk_size as u64) as usize; + drop(count); + + let mut index : u16 = 0; + let original_filename = parse_filename_from_url(String::from(url)); + for start in (0..total_size).step_by(chunk_size) { + let client_ = client.clone(); + let url_ = url.to_string(); + // let file_ = file.clone(); + // let pb_ = pb.clone(); + let start_ = start.clone() as usize; + let task_count_ = Arc::clone(&task_count); + + let end = usize::min((start as usize) + chunk_size, total_size as usize) - 1; + + // println!("progressbar size={} for thread start={}", chunk_size, start); + + let pb = ProgressBar::new(chunk_size as u64); + let pb_styletry = ProgressStyle::default_bar() + .template("{msg}\n{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {bytes}/{total_bytes} ({bytes_per_sec}, {eta})"); + if let Err(e) = pb_styletry { + return Err(format!("Could not set progress bar style")); + }; + + pb.set_style(pb_styletry.unwrap().progress_chars("#>-")); + + let pb_ = pb.clone(); + mpb.add(pb); + + let this_piece_filename_string = format!("{}.part{}", original_filename, start); + let this_piece_filename = this_piece_filename_string.clone(); + pieces.insert(index, this_piece_filename_string); + + let task = tokio::spawn( + download_chunk(client_, url_, start_, end, this_piece_filename, pb_, task_count_) + ); + // download_chunk(client_, url_, start_, end, file_, task_count_) + // ); + + tasks.insert((start, end), task); + index += 1; + + // let response = client + // .get(url) + // .header("Range", format!("bytes={}-{}", start, end)) + // .send() + // .await?; + + // let mut content = response.bytes().await?; + // file.write_all(&mut content) + // .await + // .or(Err(format!("Could not write data stream to output file.")))?; + + // pb.set_position(end as u64); + } + + for ((start, end), task) in tasks { + // println!("awaiting for task start={} end={}", start, end); + let _running_task = task.await; + // println!("task start={} finished", start); + } + + let pb = ProgressBar::new(pieces.len() as u64); + let pb_styletry = ProgressStyle::default_bar() + .template("{msg}\n{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {bytes}/{total_bytes} ({bytes_per_sec}, {eta})"); + if let Err(e) = pb_styletry { + return Err(format!("Could not set style for finishing progress bar. {}", e)); + } + pb_styletry.unwrap().progress_chars("#>-"); + + // let pb_ = pb.clone(); + pb.set_message("Finishing up"); + let pb_c = pb.clone(); + mpb.add(pb); + + // let url_string = String::from(url); + // let url_vector = url_string.split('/').collect::>(); + // let output_final_name = if url_string.chars().last().unwrap() == '/' { + // url_vector.len().checked_sub(2).map(|i| url_vector[i]).unwrap() + // } else { + // url_string.split('/').collect::>().last().copied().unwrap() + // }; + + let output_file_try = std::fs::File::create( + parse_filename_from_url(String::from(url)) + ); + if let Err(e) = output_file_try { + return Err(format!("Could not create final output file, does the running program has permission?")); + }; + let mut output_file = output_file_try.unwrap(); + + // let file = File::open(file_piece).await + // .expect("File part not found, did you remove or delete them? Finishing process could not complete."); + + let p__ = pieces.clone(); + dbg!(p__); + + for (index, (start_pos, file_piece)) in pieces.into_iter().enumerate() { + // let mut buf = Vec::with_capacity(64); + + let file_piece_clone = file_piece.clone(); + let file_piece_ = file_piece_clone.clone(); + + let piece_file_future = std::fs::File::open(&file_piece_); + if let Err(e) = piece_file_future { + return Err(format!("Could not open file part. {}", e)); + }; + + let mut input = piece_file_future.unwrap(); + let status = io::copy(&mut input, &mut output_file); + + let pos = index as u64; + pb_c.set_position(pos); + match status { + Ok(_bytes) => { + // _println!("bytes copied: {}", bytes); + let this = file_piece_clone.clone(); + if let Err(e) = std::fs::remove_file(this) { + eprintln!("Could not remove file piece \"{}\" after successful output file finish step", file_piece_clone); + } + }, + Err(_e) => { + // eprintln!("error: {}", e); + } + } + + // if let Ok(piece_file) = &mut piece_file_future { + // // piece.read -> write(output) + + // match piece_file.read(&mut buf).await { + // Ok(bytes) => { + // while bytes == buf.len() { + // match output_file.write(&buf).await { + // Ok(bytes) => { + // println!("wrote {} bytes to final file.", bytes); + // }, + // Err(e) => { + // eprintln!("could not stream bytes to final file."); + // } + // } + // } + // }, + // Err(e) => { + // eprintln!("Could not parse file piece/part data. {}", e); + // } + // } + // } + + // while let Ok(n) = output_file.read(&mut buf).await { + // let piece_file = File::open(&file_piece_); + // match &mut piece_file.await { + // Ok(piece) => { + // match piece.write(&buf).await { + // Ok(_) => { + // let pos = file_index as u64; + // pb_c.set_position(pos); + // }, + // Err(e) => { + // eprintln!("Could not write file piece data to buffer."); + // eprintln!("{}", e); + // } + // } + // }, + // Err(e) => { + // eprintln!("Could not retrieve data for file piece."); + // eprintln!("{}", e); + // } + + // } + // } + + }; + + pb_c.finish_and_clear(); + // pb.finish(); + + // println!("File download completed."); + Ok(()) +}