From 1b6a04ca5504955c571d1c97504fb45ea0befee4 Mon Sep 17 00:00:00 2001 From: Valentin Popov Date: Mon, 8 Jan 2024 01:21:28 +0400 Subject: Initial vendor packages Signed-off-by: Valentin Popov --- vendor/indicatif/.cargo-checksum.json | 1 + vendor/indicatif/Cargo.lock | 872 +++++++++++ vendor/indicatif/Cargo.toml | 117 ++ vendor/indicatif/LICENSE | 22 + vendor/indicatif/README.md | 30 + vendor/indicatif/deny.toml | 3 + vendor/indicatif/examples/cargo.rs | 122 ++ vendor/indicatif/examples/cargowrap.rs | 41 + vendor/indicatif/examples/download-continued.rs | 29 + vendor/indicatif/examples/download-speed.rs | 24 + vendor/indicatif/examples/download.rs | 25 + vendor/indicatif/examples/fastbar.rs | 23 + vendor/indicatif/examples/finebars.rs | 43 + vendor/indicatif/examples/iterator.rs | 31 + vendor/indicatif/examples/log.rs | 14 + vendor/indicatif/examples/long-spinner.rs | 27 + vendor/indicatif/examples/message.rs | 11 + vendor/indicatif/examples/morebars.rs | 28 + vendor/indicatif/examples/multi-tree-ext.rs | 280 ++++ vendor/indicatif/examples/multi-tree.rs | 189 +++ vendor/indicatif/examples/multi.rs | 67 + vendor/indicatif/examples/single.rs | 13 + vendor/indicatif/examples/slow.rs | 12 + vendor/indicatif/examples/spinner-loop.rs | 18 + vendor/indicatif/examples/steady.rs | 50 + vendor/indicatif/examples/tokio.rs | 34 + vendor/indicatif/examples/yarnish.rs | 97 ++ vendor/indicatif/src/draw_target.rs | 561 +++++++ vendor/indicatif/src/format.rs | 337 +++++ vendor/indicatif/src/in_memory.rs | 399 +++++ vendor/indicatif/src/iter.rs | 355 +++++ vendor/indicatif/src/lib.rs | 247 +++ vendor/indicatif/src/multi.rs | 688 +++++++++ vendor/indicatif/src/progress_bar.rs | 808 ++++++++++ vendor/indicatif/src/rayon.rs | 235 +++ vendor/indicatif/src/state.rs | 798 ++++++++++ vendor/indicatif/src/style.rs | 987 ++++++++++++ vendor/indicatif/src/term_like.rs | 79 + vendor/indicatif/tests/multi-autodrop.rs | 27 + vendor/indicatif/tests/render.rs | 1822 +++++++++++++++++++++++ 40 files changed, 9566 insertions(+) create mode 100644 vendor/indicatif/.cargo-checksum.json create mode 100644 vendor/indicatif/Cargo.lock create mode 100644 vendor/indicatif/Cargo.toml create mode 100644 vendor/indicatif/LICENSE create mode 100644 vendor/indicatif/README.md create mode 100644 vendor/indicatif/deny.toml create mode 100644 vendor/indicatif/examples/cargo.rs create mode 100644 vendor/indicatif/examples/cargowrap.rs create mode 100644 vendor/indicatif/examples/download-continued.rs create mode 100644 vendor/indicatif/examples/download-speed.rs create mode 100644 vendor/indicatif/examples/download.rs create mode 100644 vendor/indicatif/examples/fastbar.rs create mode 100644 vendor/indicatif/examples/finebars.rs create mode 100644 vendor/indicatif/examples/iterator.rs create mode 100644 vendor/indicatif/examples/log.rs create mode 100644 vendor/indicatif/examples/long-spinner.rs create mode 100644 vendor/indicatif/examples/message.rs create mode 100644 vendor/indicatif/examples/morebars.rs create mode 100644 vendor/indicatif/examples/multi-tree-ext.rs create mode 100644 vendor/indicatif/examples/multi-tree.rs create mode 100644 vendor/indicatif/examples/multi.rs create mode 100644 vendor/indicatif/examples/single.rs create mode 100644 vendor/indicatif/examples/slow.rs create mode 100644 vendor/indicatif/examples/spinner-loop.rs create mode 100644 vendor/indicatif/examples/steady.rs create mode 100644 vendor/indicatif/examples/tokio.rs create mode 100644 vendor/indicatif/examples/yarnish.rs create mode 100644 vendor/indicatif/src/draw_target.rs create mode 100644 vendor/indicatif/src/format.rs create mode 100644 vendor/indicatif/src/in_memory.rs create mode 100644 vendor/indicatif/src/iter.rs create mode 100644 vendor/indicatif/src/lib.rs create mode 100644 vendor/indicatif/src/multi.rs create mode 100644 vendor/indicatif/src/progress_bar.rs create mode 100644 vendor/indicatif/src/rayon.rs create mode 100644 vendor/indicatif/src/state.rs create mode 100644 vendor/indicatif/src/style.rs create mode 100644 vendor/indicatif/src/term_like.rs create mode 100644 vendor/indicatif/tests/multi-autodrop.rs create mode 100644 vendor/indicatif/tests/render.rs (limited to 'vendor/indicatif') diff --git a/vendor/indicatif/.cargo-checksum.json b/vendor/indicatif/.cargo-checksum.json new file mode 100644 index 0000000..9d239a9 --- /dev/null +++ b/vendor/indicatif/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.lock":"845bf05f1597386e99bd44e894b23e8e0d9d672b399c9c93a834d1d7f2b2f89a","Cargo.toml":"c2a073c9a96010063b677181a5acee2d65f1573d2286b537f0d11a7c74e00c94","LICENSE":"2022c11b24fc1e50fa06a5959e40198a3464f560729ad3d00139abb8a328362d","README.md":"5a49e063ebe6751f6e7eb10051a0e0a212ca7b067665dfd7905d8f9175ec2040","deny.toml":"22979f2da49546b27a5c892d5216bf74215987810a2d011b58870471883b9437","examples/cargo.rs":"486b024214f200432f8ea3c803bf8ee2fa03a289705cf490c501d390388b8ad7","examples/cargowrap.rs":"6341fc972a8ef36354d12a2a17eb3d0a2ff2a43e4c73410ce9570d9a139041c2","examples/download-continued.rs":"a4d78fe65f8b83e23edabe2b4d4da1cba0d88ae7c8eb73efec11dd33dab57524","examples/download-speed.rs":"694ad313c33a8ffe13f6db9b8b84c74a32ca2397b047fa38e488c74100753ea5","examples/download.rs":"ba7a8b6979a7c98aaee3770792956646b8d6570424e94695afee69a89d3d34d9","examples/fastbar.rs":"15fd733b7e058544005e68399d93550e7eb46c90a7c2248804887c5c521710c5","examples/finebars.rs":"c7a1a93bfcc2f8e7ec3d071e1e41575788b81506915894a8b7dcf9f573c58076","examples/iterator.rs":"9d3d965d81f1c7bed4247205e4f8978cdce870de8c8b0ed93097a403351360ba","examples/log.rs":"fd88f4d8a56726c1e3f91e2a3b7b68262dd6a48c7f30f4af5184a6efe177736c","examples/long-spinner.rs":"f753a118d0e553a0fd74bc875dc691804db32fbdad8119d53ea5e304f39cfaad","examples/message.rs":"9b6c19bb19bebce185ab23a54bc90e74ced6ea09d5aac7cc082e4fc55b5126d4","examples/morebars.rs":"a073505554dc477b509f351bb82c703d70ac256407ce32cd41a734da5604d6f9","examples/multi-tree-ext.rs":"1fdd8a9d649bfa43d8843ef975db919240108c23df027ffc2213cfc4815379df","examples/multi-tree.rs":"a42879ef63db7860aa0306147f117749efa8c9de0435040a54cb785225d23ab0","examples/multi.rs":"d90f657883eaf3b78aea0def07aa49154414e614ef96bb8df3d13154ffe76ab7","examples/single.rs":"576c342c93b5493cd78257d0eecc8abd24b2bd4a3ad3d674d1023f2a3cad833d","examples/slow.rs":"f0152c6c35bbdfcbad44153b63c895bcd1e6929cb98ee49a67fefd942e2a2631","examples/spinner-loop.rs":"492f10d3ee0781b8c78e36a12ed62c833996f69eb7aec61366a35fd28c1bf87a","examples/steady.rs":"981338f2fbf3ec3967fabde198ab973c8dc8926e6cae1a8acf53494cb47e9c89","examples/tokio.rs":"ef637f87461db3cd14de36498ad05ec3bf30f77cc67ea9315766ccbe84a96884","examples/yarnish.rs":"1931843d12d90eb1f9b63f274df39b35ed2b8eeb28f4597b98afbb709d95f6bd","src/draw_target.rs":"26dd5a33d156541d2261f37d53f3f618787541d22e078b3be5fe47353dccc421","src/format.rs":"991deb3a33eb529bede01144577a530a8330e5c7d7953e90257b28a4906bd9b2","src/in_memory.rs":"fa920e7ed034ea2af4c1adc36558384e609e34702765885de697c4db36ff943c","src/iter.rs":"a41128537c11fe0200f6d1444ddeb75694ae0298bb64081dec8c2567813606aa","src/lib.rs":"adc99e8fa6a7bbafa16209f30e6bb5da85b100368bb6ef2a238c278147f24f92","src/multi.rs":"b8f90c9f7224076657f070c43b788df81b6e041f53a21967747270c898cdba77","src/progress_bar.rs":"6c9be96df20d603f2351c29774300f389919eb77323459904dbdf6bbe03f91c5","src/rayon.rs":"4663403f972eb3fa2ee4dbff026fab784f1346298cccb35f31e3fe01d185cbb6","src/state.rs":"de8df1272f28ea6333a23c8bf687bf22d92fb1726d6e2815fc70cef62b5d2b32","src/style.rs":"019a4f953cbf0e50e0385a6936d334342160c95d031375e11f5353c91922de90","src/term_like.rs":"4605e937c63c5da9e0ab0afd49fd0466c21fdf76e99973139cec985ced25ed5d","tests/multi-autodrop.rs":"c453259d8b84e48b504b27ad0c729aa09a90acc9bfe35814f8216efbc3a98b1c","tests/render.rs":"ae56b948b9aa69d57afb6789ee26393932f4735f870c36e54f89f79d7554c96a"},"package":"fb28741c9db9a713d93deb3bb9515c20788cef5815265bee4980e87bde7e0f25"} \ No newline at end of file diff --git a/vendor/indicatif/Cargo.lock b/vendor/indicatif/Cargo.lock new file mode 100644 index 0000000..33cbf79 --- /dev/null +++ b/vendor/indicatif/Cargo.lock @@ -0,0 +1,872 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "anstream" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is-terminal", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" + +[[package]] +name = "anstyle-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "anstyle-wincon" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +dependencies = [ + "anstyle", + "windows-sys 0.48.0", +] + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[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.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "bitflags" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "4.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd304a20bff958a57f04c4e96a2e7594cc4490a0e809cbd48bb6437edaa452d" +dependencies = [ + "clap_builder", + "clap_derive", + "once_cell", +] + +[[package]] +name = "clap_builder" +version = "4.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01c6a3f08f1fe5662a35cfe393aec09c4df95f60ee93b7556505260f75eee9e1" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "console" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "unicode-width", + "windows-sys 0.45.0", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + +[[package]] +name = "errno" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "futures" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-executor" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" + +[[package]] +name = "futures-macro" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" + +[[package]] +name = "futures-task" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" + +[[package]] +name = "futures-util" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" + +[[package]] +name = "indicatif" +version = "0.17.7" +dependencies = [ + "clap", + "console", + "futures", + "futures-core", + "instant", + "number_prefix", + "once_cell", + "portable-atomic", + "pretty_assertions", + "rand", + "rayon", + "tokio", + "unicode-segmentation", + "unicode-width", + "vt100", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "is-terminal" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +dependencies = [ + "hermit-abi", + "rustix", + "windows-sys 0.48.0", +] + +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + +[[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.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "linux-raw-sys" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" + +[[package]] +name = "log" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[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.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "pin-project-lite" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "portable-atomic" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f32154ba0af3a075eefa1eda8bb414ee928f62303a54ea85b8d6638ff1a6ee9e" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "pretty_assertions" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" +dependencies = [ + "diff", + "yansi", +] + +[[package]] +name = "proc-macro2" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rayon" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] + +[[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.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.48.0", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "slab" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +dependencies = [ + "autocfg", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "2.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tokio" +version = "1.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" +dependencies = [ + "autocfg", + "backtrace", + "bytes", + "pin-project-lite", +] + +[[package]] +name = "unicode-ident" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "vt100" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84cd863bf0db7e392ba3bd04994be3473491b31e66340672af5d11943c6274de" +dependencies = [ + "itoa", + "log", + "unicode-width", + "vte", +] + +[[package]] +name = "vte" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5022b5fbf9407086c180e9557be968742d839e68346af7792b8592489732197" +dependencies = [ + "arrayvec", + "utf8parse", + "vte_generate_state_changes", +] + +[[package]] +name = "vte_generate_state_changes" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d257817081c7dffcdbab24b9e62d2def62e2ff7d00b1c20062551e6cccc145ff" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[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.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.1", +] + +[[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.48.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", +] + +[[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.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[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.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[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.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[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.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[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.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[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.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[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.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[package]] +name = "yansi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" diff --git a/vendor/indicatif/Cargo.toml b/vendor/indicatif/Cargo.toml new file mode 100644 index 0000000..5d5c266 --- /dev/null +++ b/vendor/indicatif/Cargo.toml @@ -0,0 +1,117 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +rust-version = "1.63" +name = "indicatif" +version = "0.17.7" +exclude = ["screenshots/*"] +description = "A progress bar and cli reporting library for Rust" +documentation = "https://docs.rs/indicatif" +readme = "README.md" +keywords = [ + "cli", + "progress", + "pb", + "colors", + "progressbar", +] +categories = ["command-line-interface"] +license = "MIT" +repository = "https://github.com/console-rs/indicatif" + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = [ + "--cfg", + "docsrs", +] + +[dependencies.console] +version = "0.15" +features = ["ansi-parsing"] +default-features = false + +[dependencies.futures-core] +version = "0.3" +optional = true +default-features = false + +[dependencies.number_prefix] +version = "0.4" + +[dependencies.portable-atomic] +version = "1.0.0" + +[dependencies.rayon] +version = "1.1" +optional = true + +[dependencies.tokio] +version = "1" +features = ["io-util"] +optional = true + +[dependencies.unicode-segmentation] +version = "1" +optional = true + +[dependencies.unicode-width] +version = "0.1" +optional = true + +[dependencies.vt100] +version = "0.15.1" +optional = true + +[dev-dependencies.clap] +version = "4" +features = [ + "color", + "derive", +] + +[dev-dependencies.futures] +version = "0.3" + +[dev-dependencies.once_cell] +version = "1" + +[dev-dependencies.pretty_assertions] +version = "1.4.0" + +[dev-dependencies.rand] +version = "0.8" + +[dev-dependencies.tokio] +version = "1" +features = [ + "fs", + "time", + "rt", +] + +[features] +default = [ + "unicode-width", + "console/unicode-width", +] +futures = ["dep:futures-core"] +improved_unicode = [ + "unicode-segmentation", + "unicode-width", + "console/unicode-width", +] +in_memory = ["vt100"] + +[target."cfg(target_arch = \"wasm32\")".dependencies.instant] +version = "0.1" diff --git a/vendor/indicatif/LICENSE b/vendor/indicatif/LICENSE new file mode 100644 index 0000000..dc9a85c --- /dev/null +++ b/vendor/indicatif/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2017 Armin Ronacher + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/vendor/indicatif/README.md b/vendor/indicatif/README.md new file mode 100644 index 0000000..3fde30b --- /dev/null +++ b/vendor/indicatif/README.md @@ -0,0 +1,30 @@ +# indicatif + +[![Documentation](https://docs.rs/indicatif/badge.svg)](https://docs.rs/indicatif/) +[![Crates.io](https://img.shields.io/crates/v/indicatif.svg)](https://crates.io/crates/indicatif) +[![Build status](https://github.com/console-rs/indicatif/workflows/CI/badge.svg)](https://github.com/console-rs/indicatif/actions/workflows/rust.yml) +[![Chat](https://img.shields.io/discord/976380008299917365?logo=discord)](https://discord.gg/YHmNA3De4W) + +A Rust library for indicating progress in command line applications to users. + +This currently primarily provides progress bars and spinners as well as basic +color support, but there are bigger plans for the future of this! + +## Examples + +[examples/yarnish.rs](examples/yarnish.rs) + + +[examples/download.rs](examples/download.rs) + + +[examples/multi.rs](examples/multi.rs) + + +[examples/single.rs](examples/single.rs) + + +## Integrations + +You can use [indicatif-log-bridge](https://crates.io/crates/indicatif-log-bridge) to integrate with the +[log crate](https://crates.io/crates/log) and avoid having both fight for your terminal. diff --git a/vendor/indicatif/deny.toml b/vendor/indicatif/deny.toml new file mode 100644 index 0000000..38c47a8 --- /dev/null +++ b/vendor/indicatif/deny.toml @@ -0,0 +1,3 @@ +[licenses] +allow-osi-fsf-free = "either" +copyleft = "deny" diff --git a/vendor/indicatif/examples/cargo.rs b/vendor/indicatif/examples/cargo.rs new file mode 100644 index 0000000..1e2ff2e --- /dev/null +++ b/vendor/indicatif/examples/cargo.rs @@ -0,0 +1,122 @@ +use std::sync::{mpsc, Arc, Mutex}; +use std::thread; +use std::time::{Duration, Instant}; + +use console::{Style, Term}; +use indicatif::{HumanDuration, ProgressBar, ProgressStyle}; +use rand::Rng; + +static CRATES: &[(&str, &str)] = &[ + ("console", "v0.14.1"), + ("lazy_static", "v1.4.0"), + ("libc", "v0.2.93"), + ("regex", "v1.4.6"), + ("regex-syntax", "v0.6.23"), + ("terminal_size", "v0.1.16"), + ("libc", "v0.2.93"), + ("unicode-width", "v0.1.8"), + ("lazy_static", "v1.4.0"), + ("number_prefix", "v0.4.0"), + ("regex", "v1.4.6"), + ("rand", "v0.8.3"), + ("getrandom", "v0.2.2"), + ("cfg-if", "v1.0.0"), + ("libc", "v0.2.93"), + ("rand_chacha", "v0.3.0"), + ("ppv-lite86", "v0.2.10"), + ("rand_core", "v0.6.2"), + ("getrandom", "v0.2.2"), + ("rand_core", "v0.6.2"), + ("tokio", "v1.5.0"), + ("bytes", "v1.0.1"), + ("pin-project-lite", "v0.2.6"), + ("slab", "v0.4.3"), + ("indicatif", "v0.15.0"), +]; + +fn main() { + // number of cpus + const NUM_CPUS: usize = 4; + let start = Instant::now(); + + // mimic cargo progress bar although it behaves a bit different + let pb = ProgressBar::new(CRATES.len() as u64); + pb.set_style( + ProgressStyle::with_template( + // note that bar size is fixed unlike cargo which is dynamic + // and also the truncation in cargo uses trailers (`...`) + if Term::stdout().size().1 > 80 { + "{prefix:>12.cyan.bold} [{bar:57}] {pos}/{len} {wide_msg}" + } else { + "{prefix:>12.cyan.bold} [{bar:57}] {pos}/{len}" + }, + ) + .unwrap() + .progress_chars("=> "), + ); + pb.set_prefix("Building"); + + // process in another thread + // crates to be iterated but not exactly a tree + let crates = Arc::new(Mutex::new(CRATES.iter())); + let (tx, rx) = mpsc::channel(); + for n in 0..NUM_CPUS { + let tx = tx.clone(); + let crates = crates.clone(); + thread::spawn(move || { + let mut rng = rand::thread_rng(); + loop { + let krate = crates.lock().unwrap().next(); + // notify main thread if n thread is processing a crate + tx.send((n, krate)).unwrap(); + if let Some(krate) = krate { + thread::sleep(Duration::from_millis( + // last compile and linking is always slow, let's mimic that + if CRATES.last() == Some(krate) { + rng.gen_range(1_000..2_000) + } else { + rng.gen_range(250..1_000) + }, + )); + } else { + break; + } + } + }); + } + // drop tx to stop waiting + drop(tx); + + let green_bold = Style::new().green().bold(); + + // do progress drawing in main thread + let mut processing = [None; NUM_CPUS]; + while let Ok((n, krate)) = rx.recv() { + processing[n] = krate; + let crates: Vec<&str> = processing + .iter() + .filter_map(|t| t.copied().map(|(name, _)| name)) + .collect(); + pb.set_message(crates.join(", ")); + if let Some((name, version)) = krate { + // crate is being built + let line = format!( + "{:>12} {} {}", + green_bold.apply_to("Compiling"), + name, + version + ); + pb.println(line); + + pb.inc(1); + } + } + pb.finish_and_clear(); + + // compilation is finished + println!( + "{:>12} dev [unoptimized + debuginfo] target(s) in {}", + green_bold.apply_to("Finished"), + HumanDuration(start.elapsed()) + ); +} diff --git a/vendor/indicatif/examples/cargowrap.rs b/vendor/indicatif/examples/cargowrap.rs new file mode 100644 index 0000000..354b6e0 --- /dev/null +++ b/vendor/indicatif/examples/cargowrap.rs @@ -0,0 +1,41 @@ +use std::io::{BufRead, BufReader}; +use std::process; +use std::time::{Duration, Instant}; + +use indicatif::{HumanDuration, ProgressBar, ProgressStyle}; + +pub fn main() { + let started = Instant::now(); + + println!("Compiling package in release mode..."); + + let pb = ProgressBar::new_spinner(); + pb.enable_steady_tick(Duration::from_millis(200)); + pb.set_style( + ProgressStyle::with_template("{spinner:.dim.bold} cargo: {wide_msg}") + .unwrap() + .tick_chars("/|\\- "), + ); + + let mut p = process::Command::new("cargo") + .arg("build") + .arg("--release") + .stderr(process::Stdio::piped()) + .spawn() + .unwrap(); + + for line in BufReader::new(p.stderr.take().unwrap()).lines() { + let line = line.unwrap(); + let stripped_line = line.trim(); + if !stripped_line.is_empty() { + pb.set_message(stripped_line.to_owned()); + } + pb.tick(); + } + + p.wait().unwrap(); + + pb.finish_and_clear(); + + println!("Done in {}", HumanDuration(started.elapsed())); +} diff --git a/vendor/indicatif/examples/download-continued.rs b/vendor/indicatif/examples/download-continued.rs new file mode 100644 index 0000000..c9a76b6 --- /dev/null +++ b/vendor/indicatif/examples/download-continued.rs @@ -0,0 +1,29 @@ +use std::cmp::min; +use std::thread; +use std::time::Duration; + +use indicatif::{ProgressBar, ProgressStyle}; + +fn main() { + let mut downloaded = 69369369; + let total_size = 231231231; + + let pb = ProgressBar::new(total_size); + pb.set_style( + ProgressStyle::with_template( + "{spinner:.green} [{wide_bar:.cyan/blue}] {bytes}/{total_bytes} ({eta})", + ) + .unwrap() + .progress_chars("#>-"), + ); + pb.set_position(downloaded); + pb.reset_eta(); + + while downloaded < total_size { + downloaded = min(downloaded + 123211, total_size); + pb.set_position(downloaded); + thread::sleep(Duration::from_millis(12)); + } + + pb.finish_with_message("downloaded"); +} diff --git a/vendor/indicatif/examples/download-speed.rs b/vendor/indicatif/examples/download-speed.rs new file mode 100644 index 0000000..da5a80f --- /dev/null +++ b/vendor/indicatif/examples/download-speed.rs @@ -0,0 +1,24 @@ +use std::cmp::min; +use std::thread; +use std::time::Duration; + +use indicatif::{ProgressBar, ProgressStyle}; + +fn main() { + let mut downloaded = 0; + let total_size = 231231231; + + let pb = ProgressBar::new(total_size); + pb.set_style(ProgressStyle::with_template("{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {bytes}/{total_bytes} ({bytes_per_sec}, {eta})") + .unwrap() + .progress_chars("#>-")); + + while downloaded < total_size { + let new = min(downloaded + 223211, total_size); + downloaded = new; + pb.set_position(new); + thread::sleep(Duration::from_millis(12)); + } + + pb.finish_with_message("downloaded"); +} diff --git a/vendor/indicatif/examples/download.rs b/vendor/indicatif/examples/download.rs new file mode 100644 index 0000000..c3f9202 --- /dev/null +++ b/vendor/indicatif/examples/download.rs @@ -0,0 +1,25 @@ +use std::thread; +use std::time::Duration; +use std::{cmp::min, fmt::Write}; + +use indicatif::{ProgressBar, ProgressState, ProgressStyle}; + +fn main() { + let mut downloaded = 0; + let total_size = 231231231; + + let pb = ProgressBar::new(total_size); + pb.set_style(ProgressStyle::with_template("{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {bytes}/{total_bytes} ({eta})") + .unwrap() + .with_key("eta", |state: &ProgressState, w: &mut dyn Write| write!(w, "{:.1}s", state.eta().as_secs_f64()).unwrap()) + .progress_chars("#>-")); + + while downloaded < total_size { + let new = min(downloaded + 223211, total_size); + downloaded = new; + pb.set_position(new); + thread::sleep(Duration::from_millis(12)); + } + + pb.finish_with_message("downloaded"); +} diff --git a/vendor/indicatif/examples/fastbar.rs b/vendor/indicatif/examples/fastbar.rs new file mode 100644 index 0000000..1b71f0c --- /dev/null +++ b/vendor/indicatif/examples/fastbar.rs @@ -0,0 +1,23 @@ +use indicatif::ProgressBar; + +fn many_units_of_easy_work(n: u64, label: &str) { + let pb = ProgressBar::new(n); + + let mut sum = 0; + for i in 0..n { + // Any quick computation, followed by an update to the progress bar. + sum += 2 * i + 3; + pb.inc(1); + } + pb.finish(); + + println!("[{}] Sum ({}) calculated in {:?}", label, sum, pb.elapsed()); +} + +fn main() { + const N: u64 = 1 << 20; + + // Perform a long sequence of many simple computations monitored by a + // default progress bar. + many_units_of_easy_work(N, "Default progress bar "); +} diff --git a/vendor/indicatif/examples/finebars.rs b/vendor/indicatif/examples/finebars.rs new file mode 100644 index 0000000..dffb967 --- /dev/null +++ b/vendor/indicatif/examples/finebars.rs @@ -0,0 +1,43 @@ +use std::thread; +use std::time::Duration; + +use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; +use rand::{thread_rng, Rng}; + +fn main() { + let styles = [ + ("Rough bar:", "█ ", "red"), + ("Fine bar: ", "█▉▊▋▌▍▎▏ ", "yellow"), + ("Vertical: ", "█▇▆▅▄▃▂▁ ", "green"), + ("Fade in: ", "█▓▒░ ", "blue"), + ("Blocky: ", "█▛▌▖ ", "magenta"), + ]; + + let m = MultiProgress::new(); + + let handles: Vec<_> = styles + .iter() + .map(|s| { + let pb = m.add(ProgressBar::new(512)); + pb.set_style( + ProgressStyle::with_template(&format!("{{prefix:.bold}}▕{{bar:.{}}}▏{{msg}}", s.2)) + .unwrap() + .progress_chars(s.1), + ); + pb.set_prefix(s.0); + let wait = Duration::from_millis(thread_rng().gen_range(10..30)); + thread::spawn(move || { + for i in 0..512 { + thread::sleep(wait); + pb.inc(1); + pb.set_message(format!("{:3}%", 100 * i / 512)); + } + pb.finish_with_message("100%"); + }) + }) + .collect(); + + for h in handles { + let _ = h.join(); + } +} diff --git a/vendor/indicatif/examples/iterator.rs b/vendor/indicatif/examples/iterator.rs new file mode 100644 index 0000000..8f40047 --- /dev/null +++ b/vendor/indicatif/examples/iterator.rs @@ -0,0 +1,31 @@ +use std::thread; +use std::time::Duration; + +use indicatif::{ProgressBar, ProgressIterator, ProgressStyle}; + +fn main() { + // Default styling, attempt to use Iterator::size_hint to count input size + for _ in (0..1000).progress() { + // ... + thread::sleep(Duration::from_millis(5)); + } + + // Provide explicit number of elements in iterator + for _ in (0..1000).progress_count(1000) { + // ... + thread::sleep(Duration::from_millis(5)); + } + + // Provide a custom bar style + let pb = ProgressBar::new(1000); + pb.set_style( + ProgressStyle::with_template( + "{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] ({pos}/{len}, ETA {eta})", + ) + .unwrap(), + ); + for _ in (0..1000).progress_with(pb) { + // ... + thread::sleep(Duration::from_millis(5)); + } +} diff --git a/vendor/indicatif/examples/log.rs b/vendor/indicatif/examples/log.rs new file mode 100644 index 0000000..26d506c --- /dev/null +++ b/vendor/indicatif/examples/log.rs @@ -0,0 +1,14 @@ +use std::thread; +use std::time::Duration; + +use indicatif::ProgressBar; + +fn main() { + let pb = ProgressBar::new(100); + for i in 0..100 { + thread::sleep(Duration::from_millis(25)); + pb.println(format!("[+] finished #{i}")); + pb.inc(1); + } + pb.finish_with_message("done"); +} diff --git a/vendor/indicatif/examples/long-spinner.rs b/vendor/indicatif/examples/long-spinner.rs new file mode 100644 index 0000000..6b14548 --- /dev/null +++ b/vendor/indicatif/examples/long-spinner.rs @@ -0,0 +1,27 @@ +use std::thread; +use std::time::Duration; + +use indicatif::{ProgressBar, ProgressStyle}; + +fn main() { + let pb = ProgressBar::new_spinner(); + pb.enable_steady_tick(Duration::from_millis(120)); + pb.set_style( + ProgressStyle::with_template("{spinner:.blue} {msg}") + .unwrap() + // For more spinners check out the cli-spinners project: + // https://github.com/sindresorhus/cli-spinners/blob/master/spinners.json + .tick_strings(&[ + "▹▹▹▹▹", + "▸▹▹▹▹", + "▹▸▹▹▹", + "▹▹▸▹▹", + "▹▹▹▸▹", + "▹▹▹▹▸", + "▪▪▪▪▪", + ]), + ); + pb.set_message("Calculating..."); + thread::sleep(Duration::from_secs(5)); + pb.finish_with_message("Done"); +} diff --git a/vendor/indicatif/examples/message.rs b/vendor/indicatif/examples/message.rs new file mode 100644 index 0000000..d15781a --- /dev/null +++ b/vendor/indicatif/examples/message.rs @@ -0,0 +1,11 @@ +use std::{thread, time::Duration}; + +use indicatif::ProgressBar; + +fn main() { + let pb = ProgressBar::new(100).with_message("Frobbing the widget"); + for _ in 0..100 { + thread::sleep(Duration::from_millis(30)); + pb.inc(1); + } +} diff --git a/vendor/indicatif/examples/morebars.rs b/vendor/indicatif/examples/morebars.rs new file mode 100644 index 0000000..30b664f --- /dev/null +++ b/vendor/indicatif/examples/morebars.rs @@ -0,0 +1,28 @@ +use std::sync::Arc; +use std::thread; +use std::time::Duration; + +use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; + +fn main() { + let m = Arc::new(MultiProgress::new()); + let sty = ProgressStyle::with_template("{bar:40.green/yellow} {pos:>7}/{len:7}").unwrap(); + + let pb = m.add(ProgressBar::new(5)); + pb.set_style(sty.clone()); + + // make sure we show up at all. otherwise no rendering + // event. + pb.tick(); + for _ in 0..5 { + let pb2 = m.add(ProgressBar::new(128)); + pb2.set_style(sty.clone()); + for _ in 0..128 { + thread::sleep(Duration::from_millis(5)); + pb2.inc(1); + } + pb2.finish(); + pb.inc(1); + } + pb.finish_with_message("done"); +} diff --git a/vendor/indicatif/examples/multi-tree-ext.rs b/vendor/indicatif/examples/multi-tree-ext.rs new file mode 100644 index 0000000..0651548 --- /dev/null +++ b/vendor/indicatif/examples/multi-tree-ext.rs @@ -0,0 +1,280 @@ +use clap::Parser; +use std::fmt::Debug; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::Arc; +use std::thread; +use std::time::Duration; + +use console::style; +use indicatif::{MultiProgress, MultiProgressAlignment, ProgressBar, ProgressStyle}; +use once_cell::sync::Lazy; +use rand::rngs::ThreadRng; +use rand::{Rng, RngCore}; + +#[derive(Debug, Clone)] +enum Action { + ModifyTree(usize), + IncProgressBar(usize), + Stop, +} + +#[derive(Clone, Debug)] +enum Elem { + AddItem(Item), + RemoveItem(Index), +} + +#[derive(Clone, Debug)] +struct Item { + key: String, + index: usize, + indent: usize, + progress_bar: ProgressBar, +} + +#[derive(Clone, Debug)] +struct Index(usize); + +const PB_LEN: u64 = 32; +static ELEM_IDX: AtomicUsize = AtomicUsize::new(0); + +static ELEMENTS: Lazy<[Elem; 27]> = Lazy::new(|| { + [ + Elem::AddItem(Item { + indent: 9, + index: 0, + progress_bar: ProgressBar::new(PB_LEN), + key: "dog".to_string(), + }), + Elem::AddItem(Item { + indent: 0, + index: 0, + progress_bar: ProgressBar::new(PB_LEN), + key: "temp_1".to_string(), + }), + Elem::AddItem(Item { + indent: 8, + index: 1, + progress_bar: ProgressBar::new(PB_LEN), + key: "lazy".to_string(), + }), + Elem::AddItem(Item { + indent: 0, + index: 1, + progress_bar: ProgressBar::new(PB_LEN), + key: "temp_2".to_string(), + }), + Elem::AddItem(Item { + indent: 1, + index: 0, + progress_bar: ProgressBar::new(PB_LEN), + key: "the".to_string(), + }), + Elem::AddItem(Item { + indent: 0, + index: 0, + progress_bar: ProgressBar::new(PB_LEN), + key: "temp_3".to_string(), + }), + Elem::AddItem(Item { + indent: 7, + index: 3, + progress_bar: ProgressBar::new(PB_LEN), + key: "a".to_string(), + }), + Elem::AddItem(Item { + indent: 0, + index: 3, + progress_bar: ProgressBar::new(PB_LEN), + key: "temp_4".to_string(), + }), + Elem::AddItem(Item { + indent: 6, + index: 2, + progress_bar: ProgressBar::new(PB_LEN), + key: "over".to_string(), + }), + Elem::RemoveItem(Index(6)), + Elem::RemoveItem(Index(4)), + Elem::RemoveItem(Index(3)), + Elem::RemoveItem(Index(0)), + Elem::AddItem(Item { + indent: 0, + index: 2, + progress_bar: ProgressBar::new(PB_LEN), + key: "temp_5".to_string(), + }), + Elem::AddItem(Item { + indent: 4, + index: 1, + progress_bar: ProgressBar::new(PB_LEN), + key: "fox".to_string(), + }), + Elem::AddItem(Item { + indent: 0, + index: 1, + progress_bar: ProgressBar::new(PB_LEN), + key: "temp_6".to_string(), + }), + Elem::AddItem(Item { + indent: 2, + index: 1, + progress_bar: ProgressBar::new(PB_LEN), + key: "quick".to_string(), + }), + Elem::AddItem(Item { + indent: 0, + index: 1, + progress_bar: ProgressBar::new(PB_LEN), + key: "temp_7".to_string(), + }), + Elem::AddItem(Item { + indent: 5, + index: 5, + progress_bar: ProgressBar::new(PB_LEN), + key: "jumps".to_string(), + }), + Elem::AddItem(Item { + indent: 0, + index: 5, + progress_bar: ProgressBar::new(PB_LEN), + key: "temp_8".to_string(), + }), + Elem::AddItem(Item { + indent: 3, + index: 4, + progress_bar: ProgressBar::new(PB_LEN), + key: "brown".to_string(), + }), + Elem::AddItem(Item { + indent: 0, + index: 3, + progress_bar: ProgressBar::new(PB_LEN), + key: "temp_9".to_string(), + }), + Elem::RemoveItem(Index(10)), + Elem::RemoveItem(Index(7)), + Elem::RemoveItem(Index(4)), + Elem::RemoveItem(Index(3)), + Elem::RemoveItem(Index(1)), + ] +}); + +#[derive(Debug, Parser)] +pub struct Config { + #[clap(long)] + bottom_alignment: bool, +} + +/// The example demonstrates the usage of `MultiProgress` and further extends `multi-tree` example. +/// Now the example has 3 different actions implemented, and the item tree can be modified +/// by inserting or removing progress bars. The progress bars to be removed eventually +/// have messages with pattern `"temp_*"`. +/// +/// Also the command option `--bottom-alignment` is used to control the vertical alignment of the +/// `MultiProgress`. To enable this run it with +/// ```ignore +/// cargo run --example multi-tree-ext -- --bottom-alignment +/// ``` +pub fn main() { + let conf: Config = Config::parse(); + let mp = Arc::new(MultiProgress::new()); + let alignment = if conf.bottom_alignment { + MultiProgressAlignment::Bottom + } else { + MultiProgressAlignment::Top + }; + mp.set_alignment(alignment); + let sty_main = ProgressStyle::with_template("{bar:40.green/yellow} {pos:>4}/{len:4}").unwrap(); + let sty_aux = + ProgressStyle::with_template("[{pos:>2}/{len:2}] {prefix}{spinner:.green} {msg}").unwrap(); + let sty_fin = ProgressStyle::with_template("[{pos:>2}/{len:2}] {prefix}{msg}").unwrap(); + + let pb_main = mp.add(ProgressBar::new( + ELEMENTS + .iter() + .map(|e| match e { + Elem::AddItem(item) => item.progress_bar.length().unwrap(), + Elem::RemoveItem(_) => 1, + }) + .sum(), + )); + + pb_main.set_style(sty_main); + for e in ELEMENTS.iter() { + match e { + Elem::AddItem(item) => item.progress_bar.set_style(sty_aux.clone()), + Elem::RemoveItem(_) => {} + } + } + + let mut items: Vec<&Item> = Vec::with_capacity(ELEMENTS.len()); + + let mp2 = Arc::clone(&mp); + let mut rng = ThreadRng::default(); + pb_main.tick(); + loop { + match get_action(&mut rng, &items) { + Action::Stop => { + // all elements were exhausted + pb_main.finish(); + return; + } + Action::ModifyTree(elem_idx) => match &ELEMENTS[elem_idx] { + Elem::AddItem(item) => { + let pb = mp2.insert(item.index, item.progress_bar.clone()); + pb.set_prefix(" ".repeat(item.indent)); + pb.set_message(&item.key); + items.insert(item.index, item); + } + Elem::RemoveItem(Index(index)) => { + let item = items.remove(*index); + let pb = &item.progress_bar; + mp2.remove(pb); + pb_main.inc(pb.length().unwrap() - pb.position()); + } + }, + Action::IncProgressBar(item_idx) => { + let item = &items[item_idx]; + item.progress_bar.inc(1); + let pos = item.progress_bar.position(); + if pos >= item.progress_bar.length().unwrap() { + item.progress_bar.set_style(sty_fin.clone()); + item.progress_bar.finish_with_message(format!( + "{} {}", + style("✔").green(), + item.key + )); + } + pb_main.inc(1); + } + } + thread::sleep(Duration::from_millis(20)); + } +} + +/// The function guarantees to return the action, that is valid for the current tree. +fn get_action(rng: &mut dyn RngCore, items: &[&Item]) -> Action { + let elem_idx = ELEM_IDX.load(Ordering::SeqCst); + // the indices of those items, that not completed yet + let uncompleted = items + .iter() + .enumerate() + .filter(|(_, item)| { + let pos = item.progress_bar.position(); + pos < item.progress_bar.length().unwrap() + }) + .map(|(idx, _)| idx) + .collect::>(); + let k = rng.gen_range(0..16); + if (k > 0 || k == 0 && elem_idx == ELEMENTS.len()) && !uncompleted.is_empty() { + let idx = rng.gen_range(0..uncompleted.len() as u64) as usize; + Action::IncProgressBar(uncompleted[idx]) + } else if elem_idx < ELEMENTS.len() { + ELEM_IDX.fetch_add(1, Ordering::SeqCst); + Action::ModifyTree(elem_idx) + } else { + // nothing to do more + Action::Stop + } +} diff --git a/vendor/indicatif/examples/multi-tree.rs b/vendor/indicatif/examples/multi-tree.rs new file mode 100644 index 0000000..3435424 --- /dev/null +++ b/vendor/indicatif/examples/multi-tree.rs @@ -0,0 +1,189 @@ +use std::fmt::Debug; +use std::sync::{Arc, Mutex}; +use std::thread; +use std::time::Duration; + +use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; +use once_cell::sync::Lazy; +use rand::rngs::ThreadRng; +use rand::{Rng, RngCore}; + +#[derive(Debug, Clone)] +enum Action { + AddProgressBar(usize), + IncProgressBar(usize), +} + +#[derive(Clone, Debug)] +struct Elem { + key: String, + index: usize, + indent: usize, + progress_bar: ProgressBar, +} + +static ELEMENTS: Lazy<[Elem; 9]> = Lazy::new(|| { + [ + Elem { + indent: 1, + index: 0, + progress_bar: ProgressBar::new(32), + key: "jumps".to_string(), + }, + Elem { + indent: 2, + index: 1, + progress_bar: ProgressBar::new(32), + key: "lazy".to_string(), + }, + Elem { + indent: 0, + index: 0, + progress_bar: ProgressBar::new(32), + key: "the".to_string(), + }, + Elem { + indent: 3, + index: 3, + progress_bar: ProgressBar::new(32), + key: "dog".to_string(), + }, + Elem { + indent: 2, + index: 2, + progress_bar: ProgressBar::new(32), + key: "over".to_string(), + }, + Elem { + indent: 2, + index: 1, + progress_bar: ProgressBar::new(32), + key: "brown".to_string(), + }, + Elem { + indent: 1, + index: 1, + progress_bar: ProgressBar::new(32), + key: "quick".to_string(), + }, + Elem { + indent: 3, + index: 5, + progress_bar: ProgressBar::new(32), + key: "a".to_string(), + }, + Elem { + indent: 3, + index: 3, + progress_bar: ProgressBar::new(32), + key: "fox".to_string(), + }, + ] +}); + +/// The example implements the tree-like collection of progress bars, where elements are +/// added on the fly and progress bars get incremented until all elements is added and +/// all progress bars finished. +/// On each iteration `get_action` function returns some action, and when the tree gets +/// complete, the function returns `None`, which finishes the loop. +fn main() { + let mp = Arc::new(MultiProgress::new()); + let sty_main = ProgressStyle::with_template("{bar:40.green/yellow} {pos:>4}/{len:4}").unwrap(); + let sty_aux = ProgressStyle::with_template("{spinner:.green} {msg} {pos:>4}/{len:4}").unwrap(); + + let pb_main = mp.add(ProgressBar::new( + ELEMENTS + .iter() + .map(|e| e.progress_bar.length().unwrap()) + .sum(), + )); + pb_main.set_style(sty_main); + for elem in ELEMENTS.iter() { + elem.progress_bar.set_style(sty_aux.clone()); + } + + let tree: Arc>> = Arc::new(Mutex::new(Vec::with_capacity(ELEMENTS.len()))); + let tree2 = Arc::clone(&tree); + + let mp2 = Arc::clone(&mp); + let _ = thread::spawn(move || { + let mut rng = ThreadRng::default(); + pb_main.tick(); + loop { + thread::sleep(Duration::from_millis(15)); + match get_action(&mut rng, &tree) { + None => { + // all elements were exhausted + pb_main.finish(); + return; + } + Some(Action::AddProgressBar(el_idx)) => { + let elem = &ELEMENTS[el_idx]; + let pb = mp2.insert(elem.index + 1, elem.progress_bar.clone()); + pb.set_message(format!("{} {}", " ".repeat(elem.indent), elem.key)); + tree.lock().unwrap().insert(elem.index, elem); + } + Some(Action::IncProgressBar(el_idx)) => { + let elem = &tree.lock().unwrap()[el_idx]; + elem.progress_bar.inc(1); + let pos = elem.progress_bar.position(); + if pos >= elem.progress_bar.length().unwrap() { + elem.progress_bar.finish_with_message(format!( + "{}{} {}", + " ".repeat(elem.indent), + "✔", + elem.key + )); + } + pb_main.inc(1); + } + } + } + }) + .join(); + + println!("==============================="); + println!("the tree should be the same as:"); + for elem in tree2.lock().unwrap().iter() { + println!("{} {}", " ".repeat(elem.indent), elem.key); + } +} + +/// The function guarantees to return the action, that is valid for the current tree. +fn get_action(rng: &mut dyn RngCore, tree: &Mutex>) -> Option { + let elem_len = ELEMENTS.len() as u64; + let list_len = tree.lock().unwrap().len() as u64; + let sum_free = tree + .lock() + .unwrap() + .iter() + .map(|e| { + let pos = e.progress_bar.position(); + let len = e.progress_bar.length().unwrap(); + len - pos + }) + .sum::(); + + if sum_free == 0 && list_len == elem_len { + // nothing to do more + None + } else if sum_free == 0 && list_len < elem_len { + // there is no place to make an increment + Some(Action::AddProgressBar(tree.lock().unwrap().len())) + } else { + loop { + let list = tree.lock().unwrap(); + let k = rng.gen_range(0..17); + if k == 0 && list_len < elem_len { + return Some(Action::AddProgressBar(list.len())); + } else { + let l = (k % list_len) as usize; + let pos = list[l].progress_bar.position(); + let len = list[l].progress_bar.length(); + if pos < len.unwrap() { + return Some(Action::IncProgressBar(l)); + } + } + } + } +} diff --git a/vendor/indicatif/examples/multi.rs b/vendor/indicatif/examples/multi.rs new file mode 100644 index 0000000..5ec6c5c --- /dev/null +++ b/vendor/indicatif/examples/multi.rs @@ -0,0 +1,67 @@ +use std::thread; +use std::time::Duration; + +use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; + +use rand::Rng; + +fn main() { + let m = MultiProgress::new(); + let sty = ProgressStyle::with_template( + "[{elapsed_precise}] {bar:40.cyan/blue} {pos:>7}/{len:7} {msg}", + ) + .unwrap() + .progress_chars("##-"); + + let n = 200; + let pb = m.add(ProgressBar::new(n)); + pb.set_style(sty.clone()); + pb.set_message("todo"); + let pb2 = m.add(ProgressBar::new(n)); + pb2.set_style(sty.clone()); + pb2.set_message("finished"); + + let pb3 = m.insert_after(&pb2, ProgressBar::new(1024)); + pb3.set_style(sty); + + m.println("starting!").unwrap(); + + let mut threads = vec![]; + + let m_clone = m.clone(); + let h3 = thread::spawn(move || { + for i in 0..1024 { + thread::sleep(Duration::from_millis(2)); + pb3.set_message(format!("item #{}", i + 1)); + pb3.inc(1); + } + m_clone.println("pb3 is done!").unwrap(); + pb3.finish_with_message("done"); + }); + + for i in 0..n { + thread::sleep(Duration::from_millis(15)); + if i == n / 3 { + thread::sleep(Duration::from_secs(2)); + } + pb.inc(1); + let m = m.clone(); + let pb2 = pb2.clone(); + threads.push(thread::spawn(move || { + let spinner = m.add(ProgressBar::new_spinner().with_message(i.to_string())); + spinner.enable_steady_tick(Duration::from_millis(100)); + thread::sleep( + rand::thread_rng().gen_range(Duration::from_secs(1)..Duration::from_secs(5)), + ); + pb2.inc(1); + })); + } + pb.finish_with_message("all jobs started"); + + for thread in threads { + let _ = thread.join(); + } + let _ = h3.join(); + pb2.finish_with_message("all jobs done"); + m.clear().unwrap(); +} diff --git a/vendor/indicatif/examples/single.rs b/vendor/indicatif/examples/single.rs new file mode 100644 index 0000000..6e921e5 --- /dev/null +++ b/vendor/indicatif/examples/single.rs @@ -0,0 +1,13 @@ +use std::thread; +use std::time::Duration; + +use indicatif::ProgressBar; + +fn main() { + let pb = ProgressBar::new(1024); + for _ in 0..1024 { + thread::sleep(Duration::from_millis(5)); + pb.inc(1); + } + pb.finish_with_message("done"); +} diff --git a/vendor/indicatif/examples/slow.rs b/vendor/indicatif/examples/slow.rs new file mode 100644 index 0000000..91a8e7e --- /dev/null +++ b/vendor/indicatif/examples/slow.rs @@ -0,0 +1,12 @@ +use indicatif::{ProgressBar, ProgressStyle}; +use std::time::Duration; + +fn main() { + let progress = + ProgressBar::new(10).with_style(ProgressStyle::default_bar().progress_chars("🔐🔑🕓")); + for _ in 0..10 { + progress.inc(1); + std::thread::sleep(Duration::from_secs(1)); + } + progress.finish(); +} diff --git a/vendor/indicatif/examples/spinner-loop.rs b/vendor/indicatif/examples/spinner-loop.rs new file mode 100644 index 0000000..14d698b --- /dev/null +++ b/vendor/indicatif/examples/spinner-loop.rs @@ -0,0 +1,18 @@ +use std::thread; +use std::time::Duration; + +use indicatif::{ProgressBar, ProgressFinish}; + +fn main() { + let mut spinner: Option = None; + + for i in 0..3 { + let new_spinner = ProgressBar::new_spinner() + .with_message(format!("doing stuff {}", i)) + .with_finish(ProgressFinish::AndLeave); + new_spinner.enable_steady_tick(Duration::from_millis(10)); + thread::sleep(Duration::from_millis(500)); + println!("\n\nreplace {}\n\n", i); + spinner.replace(new_spinner).map(|t| t.finish()); + } +} diff --git a/vendor/indicatif/examples/steady.rs b/vendor/indicatif/examples/steady.rs new file mode 100644 index 0000000..4515ce9 --- /dev/null +++ b/vendor/indicatif/examples/steady.rs @@ -0,0 +1,50 @@ +use std::{ + thread::sleep, + time::{Duration, Instant}, +}; + +use indicatif::{ProgressBar, ProgressIterator, ProgressStyle}; + +fn main() { + let iterations = 1000; + // Set the array with all the blocksizes to test + let blocksizes: [usize; 7] = [16, 64, 256, 1024, 4096, 16384, 65536]; + + // Set the array with all the durations to save + let mut elapsed: [Duration; 7] = [Duration::ZERO; 7]; + + for (pos, blocksize) in blocksizes.iter().enumerate() { + // Set up the style for the progressbar + let sty = ProgressStyle::default_spinner() + .tick_strings(&[ + "▹▹▹▹▹", + "▸▹▹▹▹", + "▹▸▹▹▹", + "▹▹▸▹▹", + "▹▹▹▸▹", + "▹▹▹▹▸", + "▪▪▪▪▪", + ]) + .template("{prefix} {pos:>4}/{len:4} Iterations per second: {per_sec} {spinner} {msg}") + .unwrap(); + + // Set up the progress bar and apply the style + let pb = ProgressBar::new(iterations); + pb.set_style(sty); + pb.enable_steady_tick(Duration::from_millis(120)); + pb.set_prefix(format!("Doing test with Blocksize {:5?}:", blocksize)); + + // Iterate for the given number of iterations + // for _ in (0..iterations) { + for _ in (0..iterations).progress_with(pb) { + // pb.inc(1); + // Take a timestamp for timemeasurement later on + let now = Instant::now(); + sleep(Duration::from_millis(1)); + // Save the elapsed time for later evaluation + elapsed[pos] += now.elapsed(); + } + + // pb.finish_using_style(); + } +} diff --git a/vendor/indicatif/examples/tokio.rs b/vendor/indicatif/examples/tokio.rs new file mode 100644 index 0000000..17ac2b1 --- /dev/null +++ b/vendor/indicatif/examples/tokio.rs @@ -0,0 +1,34 @@ +use std::time::Duration; + +use indicatif::ProgressBar; +use tokio::runtime; +use tokio::time::interval; + +fn main() { + // Plain progress bar, totaling 1024 steps. + let steps = 1024; + let pb = ProgressBar::new(steps); + + // Stream of events, triggering every 5ms. + let rt = runtime::Builder::new_current_thread() + .enable_time() + .build() + .expect("failed to create runtime"); + + // Future computation which runs for `steps` interval events, + // incrementing one step of the progress bar each time. + let future = async { + let mut intv = interval(Duration::from_millis(5)); + + for _ in 0..steps { + intv.tick().await; + pb.inc(1); + } + }; + + // Drive the future to completion, blocking until done. + rt.block_on(future); + + // Mark the progress bar as finished. + pb.finish(); +} diff --git a/vendor/indicatif/examples/yarnish.rs b/vendor/indicatif/examples/yarnish.rs new file mode 100644 index 0000000..6b7c29b --- /dev/null +++ b/vendor/indicatif/examples/yarnish.rs @@ -0,0 +1,97 @@ +use std::thread; +use std::time::{Duration, Instant}; + +use console::{style, Emoji}; +use indicatif::{HumanDuration, MultiProgress, ProgressBar, ProgressStyle}; +use rand::seq::SliceRandom; +use rand::Rng; + +static PACKAGES: &[&str] = &[ + "fs-events", + "my-awesome-module", + "emoji-speaker", + "wrap-ansi", + "stream-browserify", + "acorn-dynamic-import", +]; + +static COMMANDS: &[&str] = &[ + "cmake .", + "make", + "make clean", + "gcc foo.c -o foo", + "gcc bar.c -o bar", + "./helper.sh rebuild-cache", + "make all-clean", + "make test", +]; + +static LOOKING_GLASS: Emoji<'_, '_> = Emoji("🔍 ", ""); +static TRUCK: Emoji<'_, '_> = Emoji("🚚 ", ""); +static CLIP: Emoji<'_, '_> = Emoji("🔗 ", ""); +static PAPER: Emoji<'_, '_> = Emoji("📃 ", ""); +static SPARKLE: Emoji<'_, '_> = Emoji("✨ ", ":-)"); + +pub fn main() { + let mut rng = rand::thread_rng(); + let started = Instant::now(); + let spinner_style = ProgressStyle::with_template("{prefix:.bold.dim} {spinner} {wide_msg}") + .unwrap() + .tick_chars("⠁⠂⠄⡀⢀⠠⠐⠈ "); + + println!( + "{} {}Resolving packages...", + style("[1/4]").bold().dim(), + LOOKING_GLASS + ); + println!( + "{} {}Fetching packages...", + style("[2/4]").bold().dim(), + TRUCK + ); + + println!( + "{} {}Linking dependencies...", + style("[3/4]").bold().dim(), + CLIP + ); + let deps = 1232; + let pb = ProgressBar::new(deps); + for _ in 0..deps { + thread::sleep(Duration::from_millis(3)); + pb.inc(1); + } + pb.finish_and_clear(); + + println!( + "{} {}Building fresh packages...", + style("[4/4]").bold().dim(), + PAPER + ); + let m = MultiProgress::new(); + let handles: Vec<_> = (0..4u32) + .map(|i| { + let count = rng.gen_range(30..80); + let pb = m.add(ProgressBar::new(count)); + pb.set_style(spinner_style.clone()); + pb.set_prefix(format!("[{}/?]", i + 1)); + thread::spawn(move || { + let mut rng = rand::thread_rng(); + let pkg = PACKAGES.choose(&mut rng).unwrap(); + for _ in 0..count { + let cmd = COMMANDS.choose(&mut rng).unwrap(); + thread::sleep(Duration::from_millis(rng.gen_range(25..200))); + pb.set_message(format!("{pkg}: {cmd}")); + pb.inc(1); + } + pb.finish_with_message("waiting..."); + }) + }) + .collect(); + for h in handles { + let _ = h.join(); + } + m.clear().unwrap(); + + println!("{} Done in {}", SPARKLE, HumanDuration(started.elapsed())); +} diff --git a/vendor/indicatif/src/draw_target.rs b/vendor/indicatif/src/draw_target.rs new file mode 100644 index 0000000..a2c055f --- /dev/null +++ b/vendor/indicatif/src/draw_target.rs @@ -0,0 +1,561 @@ +use std::io; +use std::sync::{Arc, RwLock, RwLockWriteGuard}; +use std::thread::panicking; +use std::time::Duration; +#[cfg(not(target_arch = "wasm32"))] +use std::time::Instant; + +use console::Term; +#[cfg(target_arch = "wasm32")] +use instant::Instant; + +use crate::multi::{MultiProgressAlignment, MultiState}; +use crate::TermLike; + +/// Target for draw operations +/// +/// This tells a progress bar or a multi progress object where to paint to. +/// The draw target is a stateful wrapper over a drawing destination and +/// internally optimizes how often the state is painted to the output +/// device. +#[derive(Debug)] +pub struct ProgressDrawTarget { + kind: TargetKind, +} + +impl ProgressDrawTarget { + /// Draw to a buffered stdout terminal at a max of 20 times a second. + /// + /// For more information see [`ProgressDrawTarget::term`]. + pub fn stdout() -> Self { + Self::term(Term::buffered_stdout(), 20) + } + + /// Draw to a buffered stderr terminal at a max of 20 times a second. + /// + /// This is the default draw target for progress bars. For more + /// information see [`ProgressDrawTarget::term`]. + pub fn stderr() -> Self { + Self::term(Term::buffered_stderr(), 20) + } + + /// Draw to a buffered stdout terminal at a max of `refresh_rate` times a second. + /// + /// For more information see [`ProgressDrawTarget::term`]. + pub fn stdout_with_hz(refresh_rate: u8) -> Self { + Self::term(Term::buffered_stdout(), refresh_rate) + } + + /// Draw to a buffered stderr terminal at a max of `refresh_rate` times a second. + /// + /// For more information see [`ProgressDrawTarget::term`]. + pub fn stderr_with_hz(refresh_rate: u8) -> Self { + Self::term(Term::buffered_stderr(), refresh_rate) + } + + pub(crate) fn new_remote(state: Arc>, idx: usize) -> Self { + Self { + kind: TargetKind::Multi { state, idx }, + } + } + + /// Draw to a terminal, with a specific refresh rate. + /// + /// Progress bars are by default drawn to terminals however if the + /// terminal is not user attended the entire progress bar will be + /// hidden. This is done so that piping to a file will not produce + /// useless escape codes in that file. + /// + /// Will panic if refresh_rate is `0`. + pub fn term(term: Term, refresh_rate: u8) -> Self { + Self { + kind: TargetKind::Term { + term, + last_line_count: 0, + rate_limiter: RateLimiter::new(refresh_rate), + draw_state: DrawState::default(), + }, + } + } + + /// Draw to a boxed object that implements the [`TermLike`] trait. + pub fn term_like(term_like: Box) -> Self { + Self { + kind: TargetKind::TermLike { + inner: term_like, + last_line_count: 0, + rate_limiter: None, + draw_state: DrawState::default(), + }, + } + } + + /// Draw to a boxed object that implements the [`TermLike`] trait, + /// with a specific refresh rate. + pub fn term_like_with_hz(term_like: Box, refresh_rate: u8) -> Self { + Self { + kind: TargetKind::TermLike { + inner: term_like, + last_line_count: 0, + rate_limiter: Option::from(RateLimiter::new(refresh_rate)), + draw_state: DrawState::default(), + }, + } + } + + /// A hidden draw target. + /// + /// This forces a progress bar to be not rendered at all. + pub fn hidden() -> Self { + Self { + kind: TargetKind::Hidden, + } + } + + /// Returns true if the draw target is hidden. + /// + /// This is internally used in progress bars to figure out if overhead + /// from drawing can be prevented. + pub fn is_hidden(&self) -> bool { + match self.kind { + TargetKind::Hidden => true, + TargetKind::Term { ref term, .. } => !term.is_term(), + TargetKind::Multi { ref state, .. } => state.read().unwrap().is_hidden(), + _ => false, + } + } + + /// Returns the current width of the draw target. + pub(crate) fn width(&self) -> u16 { + match self.kind { + TargetKind::Term { ref term, .. } => term.size().1, + TargetKind::Multi { ref state, .. } => state.read().unwrap().width(), + TargetKind::Hidden => 0, + TargetKind::TermLike { ref inner, .. } => inner.width(), + } + } + + /// Notifies the backing `MultiProgress` (if applicable) that the associated progress bar should + /// be marked a zombie. + pub(crate) fn mark_zombie(&self) { + if let TargetKind::Multi { idx, state } = &self.kind { + state.write().unwrap().mark_zombie(*idx); + } + } + + /// Apply the given draw state (draws it). + pub(crate) fn drawable(&mut self, force_draw: bool, now: Instant) -> Option> { + match &mut self.kind { + TargetKind::Term { + term, + last_line_count, + rate_limiter, + draw_state, + } => { + if !term.is_term() { + return None; + } + + match force_draw || rate_limiter.allow(now) { + true => Some(Drawable::Term { + term, + last_line_count, + draw_state, + }), + false => None, // rate limited + } + } + TargetKind::Multi { idx, state, .. } => { + let state = state.write().unwrap(); + Some(Drawable::Multi { + idx: *idx, + state, + force_draw, + now, + }) + } + TargetKind::TermLike { + inner, + last_line_count, + rate_limiter, + draw_state, + } => match force_draw || rate_limiter.as_mut().map_or(true, |r| r.allow(now)) { + true => Some(Drawable::TermLike { + term_like: &**inner, + last_line_count, + draw_state, + }), + false => None, // rate limited + }, + // Hidden, finished, or no need to refresh yet + _ => None, + } + } + + /// Properly disconnects from the draw target + pub(crate) fn disconnect(&self, now: Instant) { + match self.kind { + TargetKind::Term { .. } => {} + TargetKind::Multi { idx, ref state, .. } => { + let state = state.write().unwrap(); + let _ = Drawable::Multi { + state, + idx, + force_draw: true, + now, + } + .clear(); + } + TargetKind::Hidden => {} + TargetKind::TermLike { .. } => {} + }; + } + + pub(crate) fn remote(&self) -> Option<(&Arc>, usize)> { + match &self.kind { + TargetKind::Multi { state, idx } => Some((state, *idx)), + _ => None, + } + } + + pub(crate) fn adjust_last_line_count(&mut self, adjust: LineAdjust) { + self.kind.adjust_last_line_count(adjust); + } +} + +#[derive(Debug)] +enum TargetKind { + Term { + term: Term, + last_line_count: usize, + rate_limiter: RateLimiter, + draw_state: DrawState, + }, + Multi { + state: Arc>, + idx: usize, + }, + Hidden, + TermLike { + inner: Box, + last_line_count: usize, + rate_limiter: Option, + draw_state: DrawState, + }, +} + +impl TargetKind { + /// Adjust `last_line_count` such that the next draw operation keeps/clears additional lines + fn adjust_last_line_count(&mut self, adjust: LineAdjust) { + let last_line_count: &mut usize = match self { + Self::Term { + last_line_count, .. + } => last_line_count, + Self::TermLike { + last_line_count, .. + } => last_line_count, + _ => return, + }; + + match adjust { + LineAdjust::Clear(count) => *last_line_count = last_line_count.saturating_add(count), + LineAdjust::Keep(count) => *last_line_count = last_line_count.saturating_sub(count), + } + } +} + +pub(crate) enum Drawable<'a> { + Term { + term: &'a Term, + last_line_count: &'a mut usize, + draw_state: &'a mut DrawState, + }, + Multi { + state: RwLockWriteGuard<'a, MultiState>, + idx: usize, + force_draw: bool, + now: Instant, + }, + TermLike { + term_like: &'a dyn TermLike, + last_line_count: &'a mut usize, + draw_state: &'a mut DrawState, + }, +} + +impl<'a> Drawable<'a> { + /// Adjust `last_line_count` such that the next draw operation keeps/clears additional lines + pub(crate) fn adjust_last_line_count(&mut self, adjust: LineAdjust) { + let last_line_count: &mut usize = match self { + Drawable::Term { + last_line_count, .. + } => last_line_count, + Drawable::TermLike { + last_line_count, .. + } => last_line_count, + _ => return, + }; + + match adjust { + LineAdjust::Clear(count) => *last_line_count = last_line_count.saturating_add(count), + LineAdjust::Keep(count) => *last_line_count = last_line_count.saturating_sub(count), + } + } + + pub(crate) fn state(&mut self) -> DrawStateWrapper<'_> { + let mut state = match self { + Drawable::Term { draw_state, .. } => DrawStateWrapper::for_term(draw_state), + Drawable::Multi { state, idx, .. } => state.draw_state(*idx), + Drawable::TermLike { draw_state, .. } => DrawStateWrapper::for_term(draw_state), + }; + + state.reset(); + state + } + + pub(crate) fn clear(mut self) -> io::Result<()> { + let state = self.state(); + drop(state); + self.draw() + } + + pub(crate) fn draw(self) -> io::Result<()> { + match self { + Drawable::Term { + term, + last_line_count, + draw_state, + } => draw_state.draw_to_term(term, last_line_count), + Drawable::Multi { + mut state, + force_draw, + now, + .. + } => state.draw(force_draw, None, now), + Drawable::TermLike { + term_like, + last_line_count, + draw_state, + } => draw_state.draw_to_term(term_like, last_line_count), + } + } +} + +pub(crate) enum LineAdjust { + /// Adds to `last_line_count` so that the next draw also clears those lines + Clear(usize), + /// Subtracts from `last_line_count` so that the next draw retains those lines + Keep(usize), +} + +pub(crate) struct DrawStateWrapper<'a> { + state: &'a mut DrawState, + orphan_lines: Option<&'a mut Vec>, +} + +impl<'a> DrawStateWrapper<'a> { + pub(crate) fn for_term(state: &'a mut DrawState) -> Self { + Self { + state, + orphan_lines: None, + } + } + + pub(crate) fn for_multi(state: &'a mut DrawState, orphan_lines: &'a mut Vec) -> Self { + Self { + state, + orphan_lines: Some(orphan_lines), + } + } +} + +impl std::ops::Deref for DrawStateWrapper<'_> { + type Target = DrawState; + + fn deref(&self) -> &Self::Target { + self.state + } +} + +impl std::ops::DerefMut for DrawStateWrapper<'_> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.state + } +} + +impl Drop for DrawStateWrapper<'_> { + fn drop(&mut self) { + if let Some(orphaned) = &mut self.orphan_lines { + orphaned.extend(self.state.lines.drain(..self.state.orphan_lines_count)); + self.state.orphan_lines_count = 0; + } + } +} + +#[derive(Debug)] +struct RateLimiter { + interval: u16, // in milliseconds + capacity: u8, + prev: Instant, +} + +/// Rate limit but allow occasional bursts above desired rate +impl RateLimiter { + fn new(rate: u8) -> Self { + Self { + interval: 1000 / (rate as u16), // between 3 and 1000 milliseconds + capacity: MAX_BURST, + prev: Instant::now(), + } + } + + fn allow(&mut self, now: Instant) -> bool { + if now < self.prev { + return false; + } + + let elapsed = now - self.prev; + // If `capacity` is 0 and not enough time (`self.interval` ms) has passed since + // `self.prev` to add new capacity, return `false`. The goal of this method is to + // make this decision as efficient as possible. + if self.capacity == 0 && elapsed < Duration::from_millis(self.interval as u64) { + return false; + } + + // We now calculate `new`, the number of ms, since we last returned `true`, + // and `remainder`, which represents a number of ns less than 1ms which we cannot + // convert into capacity now, so we're saving it for later. + let (new, remainder) = ( + elapsed.as_millis() / self.interval as u128, + elapsed.as_nanos() % (self.interval as u128 * 1_000_000), + ); + + // We add `new` to `capacity`, subtract one for returning `true` from here, + // then make sure it does not exceed a maximum of `MAX_BURST`, then store it. + self.capacity = Ord::min(MAX_BURST as u128, (self.capacity as u128) + new - 1) as u8; + // Store `prev` for the next iteration after subtracting the `remainder`. + // Just use `unwrap` here because it shouldn't be possible for this to underflow. + self.prev = now + .checked_sub(Duration::from_nanos(remainder as u64)) + .unwrap(); + true + } +} + +const MAX_BURST: u8 = 20; + +/// The drawn state of an element. +#[derive(Clone, Debug, Default)] +pub(crate) struct DrawState { + /// The lines to print (can contain ANSI codes) + pub(crate) lines: Vec, + /// The number of lines that shouldn't be reaped by the next tick. + pub(crate) orphan_lines_count: usize, + /// True if we should move the cursor up when possible instead of clearing lines. + pub(crate) move_cursor: bool, + /// Controls how the multi progress is aligned if some of its progress bars get removed, default is `Top` + pub(crate) alignment: MultiProgressAlignment, +} + +impl DrawState { + fn draw_to_term( + &mut self, + term: &(impl TermLike + ?Sized), + last_line_count: &mut usize, + ) -> io::Result<()> { + if panicking() { + return Ok(()); + } + + if !self.lines.is_empty() && self.move_cursor { + term.move_cursor_up(*last_line_count)?; + } else { + // Fork of console::clear_last_lines that assumes that the last line doesn't contain a '\n' + let n = *last_line_count; + term.move_cursor_up(n.saturating_sub(1))?; + for i in 0..n { + term.clear_line()?; + if i + 1 != n { + term.move_cursor_down(1)?; + } + } + term.move_cursor_up(n.saturating_sub(1))?; + } + + let shift = match self.alignment { + MultiProgressAlignment::Bottom if self.lines.len() < *last_line_count => { + let shift = *last_line_count - self.lines.len(); + for _ in 0..shift { + term.write_line("")?; + } + shift + } + _ => 0, + }; + + let term_height = term.height() as usize; + let term_width = term.width() as usize; + let len = self.lines.len(); + let mut real_len = 0; + let mut last_line_filler = 0; + debug_assert!(self.orphan_lines_count <= self.lines.len()); + for (idx, line) in self.lines.iter().enumerate() { + let line_width = console::measure_text_width(line); + let diff = if line.is_empty() { + // Empty line are new line + 1 + } else { + // Calculate real length based on terminal width + // This take in account linewrap from terminal + let terminal_len = (line_width as f64 / term_width as f64).ceil() as usize; + + // If the line is effectively empty (for example when it consists + // solely of ANSI color code sequences, count it the same as a + // new line. If the line is measured to be len = 0, we will + // subtract with overflow later. + usize::max(terminal_len, 1) + }; + // Don't consider orphan lines when comparing to terminal height. + debug_assert!(idx <= real_len); + if self.orphan_lines_count <= idx + && real_len - self.orphan_lines_count + diff > term_height + { + break; + } + real_len += diff; + if idx != 0 { + term.write_line("")?; + } + term.write_str(line)?; + if idx + 1 == len { + // Keep the cursor on the right terminal side + // So that next user writes/prints will happen on the next line + last_line_filler = term_width.saturating_sub(line_width); + } + } + term.write_str(&" ".repeat(last_line_filler))?; + + term.flush()?; + *last_line_count = real_len - self.orphan_lines_count + shift; + Ok(()) + } + + fn reset(&mut self) { + self.lines.clear(); + self.orphan_lines_count = 0; + } +} + +#[cfg(test)] +mod tests { + use crate::{MultiProgress, ProgressBar, ProgressDrawTarget}; + + #[test] + fn multi_is_hidden() { + let mp = MultiProgress::with_draw_target(ProgressDrawTarget::hidden()); + + let pb = mp.add(ProgressBar::new(100)); + assert!(mp.is_hidden()); + assert!(pb.is_hidden()); + } +} diff --git a/vendor/indicatif/src/format.rs b/vendor/indicatif/src/format.rs new file mode 100644 index 0000000..7475a25 --- /dev/null +++ b/vendor/indicatif/src/format.rs @@ -0,0 +1,337 @@ +use std::fmt; +use std::time::Duration; + +use number_prefix::NumberPrefix; + +const SECOND: Duration = Duration::from_secs(1); +const MINUTE: Duration = Duration::from_secs(60); +const HOUR: Duration = Duration::from_secs(60 * 60); +const DAY: Duration = Duration::from_secs(24 * 60 * 60); +const WEEK: Duration = Duration::from_secs(7 * 24 * 60 * 60); +const YEAR: Duration = Duration::from_secs(365 * 24 * 60 * 60); + +/// Wraps an std duration for human basic formatting. +#[derive(Debug)] +pub struct FormattedDuration(pub Duration); + +/// Wraps an std duration for human readable formatting. +#[derive(Debug)] +pub struct HumanDuration(pub Duration); + +/// Formats bytes for human readability +#[derive(Debug)] +pub struct HumanBytes(pub u64); + +/// Formats bytes for human readability using SI prefixes +#[derive(Debug)] +pub struct DecimalBytes(pub u64); + +/// Formats bytes for human readability using ISO/IEC prefixes +#[derive(Debug)] +pub struct BinaryBytes(pub u64); + +/// Formats counts for human readability using commas +#[derive(Debug)] +pub struct HumanCount(pub u64); + +/// Formats counts for human readability using commas for floats +#[derive(Debug)] +pub struct HumanFloatCount(pub f64); + +impl fmt::Display for FormattedDuration { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut t = self.0.as_secs(); + let seconds = t % 60; + t /= 60; + let minutes = t % 60; + t /= 60; + let hours = t % 24; + t /= 24; + if t > 0 { + let days = t; + write!(f, "{days}d {hours:02}:{minutes:02}:{seconds:02}") + } else { + write!(f, "{hours:02}:{minutes:02}:{seconds:02}") + } + } +} + +// `HumanDuration` should be as intuitively understandable as possible. +// So we want to round, not truncate: otherwise 1 hour and 59 minutes +// would display an ETA of "1 hour" which underestimates the time +// remaining by a factor 2. +// +// To make the precision more uniform, we avoid displaying "1 unit" +// (except for seconds), because it would be displayed for a relatively +// long duration compared to the unit itself. Instead, when we arrive +// around 1.5 unit, we change from "2 units" to the next smaller unit +// (e.g. "89 seconds"). +// +// Formally: +// * for n >= 2, we go from "n+1 units" to "n units" exactly at (n + 1/2) units +// * we switch from "2 units" to the next smaller unit at (1.5 unit minus half of the next smaller unit) + +impl fmt::Display for HumanDuration { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut idx = 0; + for (i, &(cur, _, _)) in UNITS.iter().enumerate() { + idx = i; + match UNITS.get(i + 1) { + Some(&next) if self.0.saturating_add(next.0 / 2) >= cur + cur / 2 => break, + _ => continue, + } + } + + let (unit, name, alt) = UNITS[idx]; + // FIXME when `div_duration_f64` is stable + let mut t = (self.0.as_secs_f64() / unit.as_secs_f64()).round() as usize; + if idx < UNITS.len() - 1 { + t = Ord::max(t, 2); + } + + match (f.alternate(), t) { + (true, _) => write!(f, "{t}{alt}"), + (false, 1) => write!(f, "{t} {name}"), + (false, _) => write!(f, "{t} {name}s"), + } + } +} + +const UNITS: &[(Duration, &str, &str)] = &[ + (YEAR, "year", "y"), + (WEEK, "week", "w"), + (DAY, "day", "d"), + (HOUR, "hour", "h"), + (MINUTE, "minute", "m"), + (SECOND, "second", "s"), +]; + +impl fmt::Display for HumanBytes { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match NumberPrefix::binary(self.0 as f64) { + NumberPrefix::Standalone(number) => write!(f, "{number:.0} B"), + NumberPrefix::Prefixed(prefix, number) => write!(f, "{number:.2} {prefix}B"), + } + } +} + +impl fmt::Display for DecimalBytes { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match NumberPrefix::decimal(self.0 as f64) { + NumberPrefix::Standalone(number) => write!(f, "{number:.0} B"), + NumberPrefix::Prefixed(prefix, number) => write!(f, "{number:.2} {prefix}B"), + } + } +} + +impl fmt::Display for BinaryBytes { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match NumberPrefix::binary(self.0 as f64) { + NumberPrefix::Standalone(number) => write!(f, "{number:.0} B"), + NumberPrefix::Prefixed(prefix, number) => write!(f, "{number:.2} {prefix}B"), + } + } +} + +impl fmt::Display for HumanCount { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use fmt::Write; + + let num = self.0.to_string(); + let len = num.len(); + for (idx, c) in num.chars().enumerate() { + let pos = len - idx - 1; + f.write_char(c)?; + if pos > 0 && pos % 3 == 0 { + f.write_char(',')?; + } + } + Ok(()) + } +} + +impl fmt::Display for HumanFloatCount { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use fmt::Write; + + let num = format!("{:.4}", self.0); + let (int_part, frac_part) = match num.split_once('.') { + Some((int_str, fract_str)) => (int_str.to_string(), fract_str), + None => (self.0.trunc().to_string(), ""), + }; + let len = int_part.len(); + for (idx, c) in int_part.chars().enumerate() { + let pos = len - idx - 1; + f.write_char(c)?; + if pos > 0 && pos % 3 == 0 { + f.write_char(',')?; + } + } + let frac_trimmed = frac_part.trim_end_matches('0'); + if !frac_trimmed.is_empty() { + f.write_char('.')?; + f.write_str(frac_trimmed)?; + } + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + const MILLI: Duration = Duration::from_millis(1); + + #[test] + fn human_duration_alternate() { + for (unit, _, alt) in UNITS { + assert_eq!(format!("2{alt}"), format!("{:#}", HumanDuration(2 * *unit))); + } + } + + #[test] + fn human_duration_less_than_one_second() { + assert_eq!( + "0 seconds", + format!("{}", HumanDuration(Duration::from_secs(0))) + ); + assert_eq!("0 seconds", format!("{}", HumanDuration(MILLI))); + assert_eq!("0 seconds", format!("{}", HumanDuration(499 * MILLI))); + assert_eq!("1 second", format!("{}", HumanDuration(500 * MILLI))); + assert_eq!("1 second", format!("{}", HumanDuration(999 * MILLI))); + } + + #[test] + fn human_duration_less_than_two_seconds() { + assert_eq!("1 second", format!("{}", HumanDuration(1499 * MILLI))); + assert_eq!("2 seconds", format!("{}", HumanDuration(1500 * MILLI))); + assert_eq!("2 seconds", format!("{}", HumanDuration(1999 * MILLI))); + } + + #[test] + fn human_duration_one_unit() { + assert_eq!("1 second", format!("{}", HumanDuration(SECOND))); + assert_eq!("60 seconds", format!("{}", HumanDuration(MINUTE))); + assert_eq!("60 minutes", format!("{}", HumanDuration(HOUR))); + assert_eq!("24 hours", format!("{}", HumanDuration(DAY))); + assert_eq!("7 days", format!("{}", HumanDuration(WEEK))); + assert_eq!("52 weeks", format!("{}", HumanDuration(YEAR))); + } + + #[test] + fn human_duration_less_than_one_and_a_half_unit() { + // this one is actually done at 1.5 unit - half of the next smaller unit - epsilon + // and should display the next smaller unit + let d = HumanDuration(MINUTE + MINUTE / 2 - SECOND / 2 - MILLI); + assert_eq!("89 seconds", format!("{d}")); + let d = HumanDuration(HOUR + HOUR / 2 - MINUTE / 2 - MILLI); + assert_eq!("89 minutes", format!("{d}")); + let d = HumanDuration(DAY + DAY / 2 - HOUR / 2 - MILLI); + assert_eq!("35 hours", format!("{d}")); + let d = HumanDuration(WEEK + WEEK / 2 - DAY / 2 - MILLI); + assert_eq!("10 days", format!("{d}")); + let d = HumanDuration(YEAR + YEAR / 2 - WEEK / 2 - MILLI); + assert_eq!("78 weeks", format!("{d}")); + } + + #[test] + fn human_duration_one_and_a_half_unit() { + // this one is actually done at 1.5 unit - half of the next smaller unit + // and should still display "2 units" + let d = HumanDuration(MINUTE + MINUTE / 2 - SECOND / 2); + assert_eq!("2 minutes", format!("{d}")); + let d = HumanDuration(HOUR + HOUR / 2 - MINUTE / 2); + assert_eq!("2 hours", format!("{d}")); + let d = HumanDuration(DAY + DAY / 2 - HOUR / 2); + assert_eq!("2 days", format!("{d}")); + let d = HumanDuration(WEEK + WEEK / 2 - DAY / 2); + assert_eq!("2 weeks", format!("{d}")); + let d = HumanDuration(YEAR + YEAR / 2 - WEEK / 2); + assert_eq!("2 years", format!("{d}")); + } + + #[test] + fn human_duration_two_units() { + assert_eq!("2 seconds", format!("{}", HumanDuration(2 * SECOND))); + assert_eq!("2 minutes", format!("{}", HumanDuration(2 * MINUTE))); + assert_eq!("2 hours", format!("{}", HumanDuration(2 * HOUR))); + assert_eq!("2 days", format!("{}", HumanDuration(2 * DAY))); + assert_eq!("2 weeks", format!("{}", HumanDuration(2 * WEEK))); + assert_eq!("2 years", format!("{}", HumanDuration(2 * YEAR))); + } + + #[test] + fn human_duration_less_than_two_and_a_half_units() { + let d = HumanDuration(2 * SECOND + SECOND / 2 - MILLI); + assert_eq!("2 seconds", format!("{d}")); + let d = HumanDuration(2 * MINUTE + MINUTE / 2 - MILLI); + assert_eq!("2 minutes", format!("{d}")); + let d = HumanDuration(2 * HOUR + HOUR / 2 - MILLI); + assert_eq!("2 hours", format!("{d}")); + let d = HumanDuration(2 * DAY + DAY / 2 - MILLI); + assert_eq!("2 days", format!("{d}")); + let d = HumanDuration(2 * WEEK + WEEK / 2 - MILLI); + assert_eq!("2 weeks", format!("{d}")); + let d = HumanDuration(2 * YEAR + YEAR / 2 - MILLI); + assert_eq!("2 years", format!("{d}")); + } + + #[test] + fn human_duration_two_and_a_half_units() { + let d = HumanDuration(2 * SECOND + SECOND / 2); + assert_eq!("3 seconds", format!("{d}")); + let d = HumanDuration(2 * MINUTE + MINUTE / 2); + assert_eq!("3 minutes", format!("{d}")); + let d = HumanDuration(2 * HOUR + HOUR / 2); + assert_eq!("3 hours", format!("{d}")); + let d = HumanDuration(2 * DAY + DAY / 2); + assert_eq!("3 days", format!("{d}")); + let d = HumanDuration(2 * WEEK + WEEK / 2); + assert_eq!("3 weeks", format!("{d}")); + let d = HumanDuration(2 * YEAR + YEAR / 2); + assert_eq!("3 years", format!("{d}")); + } + + #[test] + fn human_duration_three_units() { + assert_eq!("3 seconds", format!("{}", HumanDuration(3 * SECOND))); + assert_eq!("3 minutes", format!("{}", HumanDuration(3 * MINUTE))); + assert_eq!("3 hours", format!("{}", HumanDuration(3 * HOUR))); + assert_eq!("3 days", format!("{}", HumanDuration(3 * DAY))); + assert_eq!("3 weeks", format!("{}", HumanDuration(3 * WEEK))); + assert_eq!("3 years", format!("{}", HumanDuration(3 * YEAR))); + } + + #[test] + fn human_count() { + assert_eq!("42", format!("{}", HumanCount(42))); + assert_eq!("7,654", format!("{}", HumanCount(7654))); + assert_eq!("12,345", format!("{}", HumanCount(12345))); + assert_eq!("1,234,567,890", format!("{}", HumanCount(1234567890))); + } + + #[test] + fn human_float_count() { + assert_eq!("42", format!("{}", HumanFloatCount(42.0))); + assert_eq!("7,654", format!("{}", HumanFloatCount(7654.0))); + assert_eq!("12,345", format!("{}", HumanFloatCount(12345.0))); + assert_eq!( + "1,234,567,890", + format!("{}", HumanFloatCount(1234567890.0)) + ); + assert_eq!("42.5", format!("{}", HumanFloatCount(42.5))); + assert_eq!("42.5", format!("{}", HumanFloatCount(42.500012345))); + assert_eq!("42.502", format!("{}", HumanFloatCount(42.502012345))); + assert_eq!("7,654.321", format!("{}", HumanFloatCount(7654.321))); + assert_eq!("7,654.321", format!("{}", HumanFloatCount(7654.3210123456))); + assert_eq!("12,345.6789", format!("{}", HumanFloatCount(12345.6789))); + assert_eq!( + "1,234,567,890.1235", + format!("{}", HumanFloatCount(1234567890.1234567)) + ); + assert_eq!( + "1,234,567,890.1234", + format!("{}", HumanFloatCount(1234567890.1234321)) + ); + } +} diff --git a/vendor/indicatif/src/in_memory.rs b/vendor/indicatif/src/in_memory.rs new file mode 100644 index 0000000..046ae14 --- /dev/null +++ b/vendor/indicatif/src/in_memory.rs @@ -0,0 +1,399 @@ +use std::fmt::{Debug, Formatter, Write as _}; +use std::io::Write as _; +use std::sync::{Arc, Mutex}; + +use vt100::Parser; + +use crate::TermLike; + +/// A thin wrapper around [`vt100::Parser`]. +/// +/// This is just an [`Arc`] around its internal state, so it can be freely cloned. +#[cfg_attr(docsrs, doc(cfg(feature = "in_memory")))] +#[derive(Debug, Clone)] +pub struct InMemoryTerm { + state: Arc>, +} + +impl InMemoryTerm { + pub fn new(rows: u16, cols: u16) -> InMemoryTerm { + assert!(rows > 0, "rows must be > 0"); + assert!(cols > 0, "cols must be > 0"); + InMemoryTerm { + state: Arc::new(Mutex::new(InMemoryTermState::new(rows, cols))), + } + } + + pub fn reset(&self) { + let mut state = self.state.lock().unwrap(); + *state = InMemoryTermState::new(state.height, state.width); + } + + pub fn contents(&self) -> String { + let state = self.state.lock().unwrap(); + + // For some reason, the `Screen::contents` method doesn't include newlines in what it + // returns, making it useless for our purposes. So we need to manually reconstruct the + // contents by iterating over the rows in the terminal buffer. + let mut rows = state + .parser + .screen() + .rows(0, state.width) + .collect::>(); + + // Reverse the rows and trim empty lines from the end + rows = rows + .into_iter() + .rev() + .skip_while(|line| line.is_empty()) + .map(|line| line.trim_end().to_string()) + .collect(); + + // Un-reverse the rows and join them up with newlines + rows.reverse(); + rows.join("\n") + } + + pub fn contents_formatted(&self) -> Vec { + let state = self.state.lock().unwrap(); + + // For some reason, the `Screen::contents` method doesn't include newlines in what it + // returns, making it useless for our purposes. So we need to manually reconstruct the + // contents by iterating over the rows in the terminal buffer. + let mut rows = state + .parser + .screen() + .rows_formatted(0, state.width) + .collect::>(); + + // Reverse the rows and trim empty lines from the end + rows = rows + .into_iter() + .rev() + .skip_while(|line| line.is_empty()) + .collect(); + + // Un-reverse the rows + rows.reverse(); + + // Calculate buffer size + let reset = b""; + let len = rows.iter().map(|line| line.len() + reset.len() + 1).sum(); + + // Join rows up with reset codes and newlines + let mut contents = rows.iter().fold(Vec::with_capacity(len), |mut acc, cur| { + acc.extend_from_slice(cur); + acc.extend_from_slice(reset); + acc.push(b'\n'); + acc + }); + + // Remove last newline again, but leave the reset code + contents.truncate(len.saturating_sub(1)); + contents + } + + pub fn moves_since_last_check(&self) -> String { + let mut s = String::new(); + for line in std::mem::take(&mut self.state.lock().unwrap().history) { + writeln!(s, "{line:?}").unwrap(); + } + s + } +} + +impl TermLike for InMemoryTerm { + fn width(&self) -> u16 { + self.state.lock().unwrap().width + } + + fn height(&self) -> u16 { + self.state.lock().unwrap().height + } + + fn move_cursor_up(&self, n: usize) -> std::io::Result<()> { + match n { + 0 => Ok(()), + _ => { + let mut state = self.state.lock().unwrap(); + state.history.push(Move::Up(n)); + state.write_str(&format!("\x1b[{n}A")) + } + } + } + + fn move_cursor_down(&self, n: usize) -> std::io::Result<()> { + match n { + 0 => Ok(()), + _ => { + let mut state = self.state.lock().unwrap(); + state.history.push(Move::Down(n)); + state.write_str(&format!("\x1b[{n}B")) + } + } + } + + fn move_cursor_right(&self, n: usize) -> std::io::Result<()> { + match n { + 0 => Ok(()), + _ => { + let mut state = self.state.lock().unwrap(); + state.history.push(Move::Right(n)); + state.write_str(&format!("\x1b[{n}C")) + } + } + } + + fn move_cursor_left(&self, n: usize) -> std::io::Result<()> { + match n { + 0 => Ok(()), + _ => { + let mut state = self.state.lock().unwrap(); + state.history.push(Move::Left(n)); + state.write_str(&format!("\x1b[{n}D")) + } + } + } + + fn write_line(&self, s: &str) -> std::io::Result<()> { + let mut state = self.state.lock().unwrap(); + state.history.push(Move::Str(s.into())); + state.history.push(Move::NewLine); + + // Don't try to handle writing lines with additional newlines embedded in them - it's not + // worth the extra code for something that indicatif doesn't even do. May revisit in future. + debug_assert!( + s.lines().count() <= 1, + "calling write_line with embedded newlines is not allowed" + ); + + // vte100 needs the full \r\n sequence to jump to the next line and reset the cursor to + // the beginning of the line. Be flexible and take either \n or \r\n + state.write_str(s)?; + state.write_str("\r\n") + } + + fn write_str(&self, s: &str) -> std::io::Result<()> { + let mut state = self.state.lock().unwrap(); + state.history.push(Move::Str(s.into())); + state.write_str(s) + } + + fn clear_line(&self) -> std::io::Result<()> { + let mut state = self.state.lock().unwrap(); + state.history.push(Move::Clear); + state.write_str("\r\x1b[2K") + } + + fn flush(&self) -> std::io::Result<()> { + let mut state = self.state.lock().unwrap(); + state.history.push(Move::Flush); + state.parser.flush() + } +} + +struct InMemoryTermState { + width: u16, + height: u16, + parser: vt100::Parser, + history: Vec, +} + +impl InMemoryTermState { + pub(crate) fn new(rows: u16, cols: u16) -> InMemoryTermState { + InMemoryTermState { + width: cols, + height: rows, + parser: Parser::new(rows, cols, 0), + history: vec![], + } + } + + pub(crate) fn write_str(&mut self, s: &str) -> std::io::Result<()> { + self.parser.write_all(s.as_bytes()) + } +} + +impl Debug for InMemoryTermState { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("InMemoryTermState").finish_non_exhaustive() + } +} + +#[derive(Debug, PartialEq, Clone)] +enum Move { + Up(usize), + Down(usize), + Left(usize), + Right(usize), + Str(String), + NewLine, + Clear, + Flush, +} + +#[cfg(test)] +mod test { + use super::*; + + fn cursor_pos(in_mem: &InMemoryTerm) -> (u16, u16) { + in_mem + .state + .lock() + .unwrap() + .parser + .screen() + .cursor_position() + } + + #[test] + fn line_wrapping() { + let in_mem = InMemoryTerm::new(10, 5); + assert_eq!(cursor_pos(&in_mem), (0, 0)); + + in_mem.write_str("ABCDE").unwrap(); + assert_eq!(in_mem.contents(), "ABCDE"); + assert_eq!(cursor_pos(&in_mem), (0, 5)); + assert_eq!( + in_mem.moves_since_last_check(), + r#"Str("ABCDE") +"# + ); + + // Should wrap onto next line + in_mem.write_str("FG").unwrap(); + assert_eq!(in_mem.contents(), "ABCDE\nFG"); + assert_eq!(cursor_pos(&in_mem), (1, 2)); + assert_eq!( + in_mem.moves_since_last_check(), + r#"Str("FG") +"# + ); + + in_mem.write_str("HIJ").unwrap(); + assert_eq!(in_mem.contents(), "ABCDE\nFGHIJ"); + assert_eq!(cursor_pos(&in_mem), (1, 5)); + assert_eq!( + in_mem.moves_since_last_check(), + r#"Str("HIJ") +"# + ); + } + + #[test] + fn write_line() { + let in_mem = InMemoryTerm::new(10, 5); + assert_eq!(cursor_pos(&in_mem), (0, 0)); + + in_mem.write_line("A").unwrap(); + assert_eq!(in_mem.contents(), "A"); + assert_eq!(cursor_pos(&in_mem), (1, 0)); + assert_eq!( + in_mem.moves_since_last_check(), + r#"Str("A") +NewLine +"# + ); + + in_mem.write_line("B").unwrap(); + assert_eq!(in_mem.contents(), "A\nB"); + assert_eq!(cursor_pos(&in_mem), (2, 0)); + assert_eq!( + in_mem.moves_since_last_check(), + r#"Str("B") +NewLine +"# + ); + + in_mem.write_line("Longer than cols").unwrap(); + assert_eq!(in_mem.contents(), "A\nB\nLonge\nr tha\nn col\ns"); + assert_eq!(cursor_pos(&in_mem), (6, 0)); + assert_eq!( + in_mem.moves_since_last_check(), + r#"Str("Longer than cols") +NewLine +"# + ); + } + + #[test] + fn basic_functionality() { + let in_mem = InMemoryTerm::new(10, 80); + + in_mem.write_line("This is a test line").unwrap(); + assert_eq!(in_mem.contents(), "This is a test line"); + assert_eq!( + in_mem.moves_since_last_check(), + r#"Str("This is a test line") +NewLine +"# + ); + + in_mem.write_line("And another line!").unwrap(); + assert_eq!(in_mem.contents(), "This is a test line\nAnd another line!"); + assert_eq!( + in_mem.moves_since_last_check(), + r#"Str("And another line!") +NewLine +"# + ); + + in_mem.move_cursor_up(1).unwrap(); + in_mem.write_str("TEST").unwrap(); + + assert_eq!(in_mem.contents(), "This is a test line\nTESTanother line!"); + assert_eq!( + in_mem.moves_since_last_check(), + r#"Up(1) +Str("TEST") +"# + ); + } + + #[test] + fn newlines() { + let in_mem = InMemoryTerm::new(10, 10); + in_mem.write_line("LINE ONE").unwrap(); + in_mem.write_line("LINE TWO").unwrap(); + in_mem.write_line("").unwrap(); + in_mem.write_line("LINE FOUR").unwrap(); + + assert_eq!(in_mem.contents(), "LINE ONE\nLINE TWO\n\nLINE FOUR"); + + assert_eq!( + in_mem.moves_since_last_check(), + r#"Str("LINE ONE") +NewLine +Str("LINE TWO") +NewLine +Str("") +NewLine +Str("LINE FOUR") +NewLine +"# + ); + } + + #[test] + fn cursor_zero_movement() { + let in_mem = InMemoryTerm::new(10, 80); + in_mem.write_line("LINE ONE").unwrap(); + assert_eq!(cursor_pos(&in_mem), (1, 0)); + + // Check that moving zero rows/cols does not actually move cursor + in_mem.move_cursor_up(0).unwrap(); + assert_eq!(cursor_pos(&in_mem), (1, 0)); + + in_mem.move_cursor_down(0).unwrap(); + assert_eq!(cursor_pos(&in_mem), (1, 0)); + + in_mem.move_cursor_right(1).unwrap(); + assert_eq!(cursor_pos(&in_mem), (1, 1)); + + in_mem.move_cursor_left(0).unwrap(); + assert_eq!(cursor_pos(&in_mem), (1, 1)); + + in_mem.move_cursor_right(0).unwrap(); + assert_eq!(cursor_pos(&in_mem), (1, 1)); + } +} diff --git a/vendor/indicatif/src/iter.rs b/vendor/indicatif/src/iter.rs new file mode 100644 index 0000000..3e73660 --- /dev/null +++ b/vendor/indicatif/src/iter.rs @@ -0,0 +1,355 @@ +use std::borrow::Cow; +use std::io::{self, IoSliceMut}; +use std::iter::FusedIterator; +#[cfg(feature = "tokio")] +use std::pin::Pin; +#[cfg(feature = "tokio")] +use std::task::{Context, Poll}; +use std::time::Duration; + +#[cfg(feature = "tokio")] +use tokio::io::{ReadBuf, SeekFrom}; + +use crate::progress_bar::ProgressBar; +use crate::state::ProgressFinish; +use crate::style::ProgressStyle; + +/// Wraps an iterator to display its progress. +pub trait ProgressIterator +where + Self: Sized + Iterator, +{ + /// Wrap an iterator with default styling. Uses `Iterator::size_hint` to get length. + /// Returns `Some(..)` only if `size_hint.1` is `Some`. If you want to create a progress bar + /// even if `size_hint.1` returns `None` use `progress_count` or `progress_with` instead. + fn try_progress(self) -> Option> { + self.size_hint() + .1 + .map(|len| self.progress_count(u64::try_from(len).unwrap())) + } + + /// Wrap an iterator with default styling. + fn progress(self) -> ProgressBarIter + where + Self: ExactSizeIterator, + { + let len = u64::try_from(self.len()).unwrap(); + self.progress_count(len) + } + + /// Wrap an iterator with an explicit element count. + fn progress_count(self, len: u64) -> ProgressBarIter { + self.progress_with(ProgressBar::new(len)) + } + + /// Wrap an iterator with a custom progress bar. + fn progress_with(self, progress: ProgressBar) -> ProgressBarIter; + + /// Wrap an iterator with a progress bar and style it. + fn progress_with_style(self, style: crate::ProgressStyle) -> ProgressBarIter + where + Self: ExactSizeIterator, + { + let len = u64::try_from(self.len()).unwrap(); + let bar = ProgressBar::new(len).with_style(style); + self.progress_with(bar) + } +} + +/// Wraps an iterator to display its progress. +#[derive(Debug)] +pub struct ProgressBarIter { + pub(crate) it: T, + pub progress: ProgressBar, +} + +impl ProgressBarIter { + /// Builder-like function for setting underlying progress bar's style. + /// + /// See [ProgressBar::with_style]. + pub fn with_style(mut self, style: ProgressStyle) -> Self { + self.progress = self.progress.with_style(style); + self + } + + /// Builder-like function for setting underlying progress bar's prefix. + /// + /// See [ProgressBar::with_prefix]. + pub fn with_prefix(mut self, prefix: impl Into>) -> Self { + self.progress = self.progress.with_prefix(prefix); + self + } + + /// Builder-like function for setting underlying progress bar's message. + /// + /// See [ProgressBar::with_message]. + pub fn with_message(mut self, message: impl Into>) -> Self { + self.progress = self.progress.with_message(message); + self + } + + /// Builder-like function for setting underlying progress bar's position. + /// + /// See [ProgressBar::with_position]. + pub fn with_position(mut self, position: u64) -> Self { + self.progress = self.progress.with_position(position); + self + } + + /// Builder-like function for setting underlying progress bar's elapsed time. + /// + /// See [ProgressBar::with_elapsed]. + pub fn with_elapsed(mut self, elapsed: Duration) -> Self { + self.progress = self.progress.with_elapsed(elapsed); + self + } + + /// Builder-like function for setting underlying progress bar's finish behavior. + /// + /// See [ProgressBar::with_finish]. + pub fn with_finish(mut self, finish: ProgressFinish) -> Self { + self.progress = self.progress.with_finish(finish); + self + } +} + +impl> Iterator for ProgressBarIter { + type Item = S; + + fn next(&mut self) -> Option { + let item = self.it.next(); + + if item.is_some() { + self.progress.inc(1); + } else if !self.progress.is_finished() { + self.progress.finish_using_style(); + } + + item + } +} + +impl ExactSizeIterator for ProgressBarIter { + fn len(&self) -> usize { + self.it.len() + } +} + +impl DoubleEndedIterator for ProgressBarIter { + fn next_back(&mut self) -> Option { + let item = self.it.next_back(); + + if item.is_some() { + self.progress.inc(1); + } else if !self.progress.is_finished() { + self.progress.finish_using_style(); + } + + item + } +} + +impl FusedIterator for ProgressBarIter {} + +impl io::Read for ProgressBarIter { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let inc = self.it.read(buf)?; + self.progress.inc(inc as u64); + Ok(inc) + } + + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + let inc = self.it.read_vectored(bufs)?; + self.progress.inc(inc as u64); + Ok(inc) + } + + fn read_to_string(&mut self, buf: &mut String) -> io::Result { + let inc = self.it.read_to_string(buf)?; + self.progress.inc(inc as u64); + Ok(inc) + } + + fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { + self.it.read_exact(buf)?; + self.progress.inc(buf.len() as u64); + Ok(()) + } +} + +impl io::BufRead for ProgressBarIter { + fn fill_buf(&mut self) -> io::Result<&[u8]> { + self.it.fill_buf() + } + + fn consume(&mut self, amt: usize) { + self.it.consume(amt); + self.progress.inc(amt as u64); + } +} + +impl io::Seek for ProgressBarIter { + fn seek(&mut self, f: io::SeekFrom) -> io::Result { + self.it.seek(f).map(|pos| { + self.progress.set_position(pos); + pos + }) + } + // Pass this through to preserve optimizations that the inner I/O object may use here + // Also avoid sending a set_position update when the position hasn't changed + fn stream_position(&mut self) -> io::Result { + self.it.stream_position() + } +} + +#[cfg(feature = "tokio")] +#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] +impl tokio::io::AsyncWrite for ProgressBarIter { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + Pin::new(&mut self.it).poll_write(cx, buf).map(|poll| { + poll.map(|inc| { + self.progress.inc(inc as u64); + inc + }) + }) + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.it).poll_flush(cx) + } + + fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.it).poll_shutdown(cx) + } +} + +#[cfg(feature = "tokio")] +#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] +impl tokio::io::AsyncRead for ProgressBarIter { + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut ReadBuf<'_>, + ) -> Poll> { + let prev_len = buf.filled().len() as u64; + if let Poll::Ready(e) = Pin::new(&mut self.it).poll_read(cx, buf) { + self.progress.inc(buf.filled().len() as u64 - prev_len); + Poll::Ready(e) + } else { + Poll::Pending + } + } +} + +#[cfg(feature = "tokio")] +#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] +impl tokio::io::AsyncSeek for ProgressBarIter { + fn start_seek(mut self: Pin<&mut Self>, position: SeekFrom) -> io::Result<()> { + Pin::new(&mut self.it).start_seek(position) + } + + fn poll_complete(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.it).poll_complete(cx) + } +} + +#[cfg(feature = "tokio")] +#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] +impl tokio::io::AsyncBufRead + for ProgressBarIter +{ + fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.get_mut(); + let result = Pin::new(&mut this.it).poll_fill_buf(cx); + if let Poll::Ready(Ok(buf)) = &result { + this.progress.inc(buf.len() as u64); + } + result + } + + fn consume(mut self: Pin<&mut Self>, amt: usize) { + Pin::new(&mut self.it).consume(amt); + } +} + +#[cfg(feature = "futures")] +#[cfg_attr(docsrs, doc(cfg(feature = "futures")))] +impl futures_core::Stream for ProgressBarIter { + type Item = S::Item; + + fn poll_next( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + let this = self.get_mut(); + let item = std::pin::Pin::new(&mut this.it).poll_next(cx); + match &item { + std::task::Poll::Ready(Some(_)) => this.progress.inc(1), + std::task::Poll::Ready(None) => this.progress.finish_using_style(), + std::task::Poll::Pending => {} + } + item + } +} + +impl io::Write for ProgressBarIter { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.it.write(buf).map(|inc| { + self.progress.inc(inc as u64); + inc + }) + } + + fn write_vectored(&mut self, bufs: &[io::IoSlice]) -> io::Result { + self.it.write_vectored(bufs).map(|inc| { + self.progress.inc(inc as u64); + inc + }) + } + + fn flush(&mut self) -> io::Result<()> { + self.it.flush() + } + + // write_fmt can not be captured with reasonable effort. + // as it uses write_all internally by default that should not be a problem. + // fn write_fmt(&mut self, fmt: fmt::Arguments) -> io::Result<()>; +} + +impl> ProgressIterator for T { + fn progress_with(self, progress: ProgressBar) -> ProgressBarIter { + ProgressBarIter { it: self, progress } + } +} + +#[cfg(test)] +mod test { + use crate::iter::{ProgressBarIter, ProgressIterator}; + use crate::progress_bar::ProgressBar; + use crate::ProgressStyle; + + #[test] + fn it_can_wrap_an_iterator() { + let v = [1, 2, 3]; + let wrap = |it: ProgressBarIter<_>| { + assert_eq!(it.map(|x| x * 2).collect::>(), vec![2, 4, 6]); + }; + + wrap(v.iter().progress()); + wrap(v.iter().progress_count(3)); + wrap({ + let pb = ProgressBar::new(v.len() as u64); + v.iter().progress_with(pb) + }); + wrap({ + let style = ProgressStyle::default_bar() + .template("{wide_bar:.red} {percent}/100%") + .unwrap(); + v.iter().progress_with_style(style) + }); + } +} diff --git a/vendor/indicatif/src/lib.rs b/vendor/indicatif/src/lib.rs new file mode 100644 index 0000000..6902ef6 --- /dev/null +++ b/vendor/indicatif/src/lib.rs @@ -0,0 +1,247 @@ +//! indicatif is a library for Rust that helps you build command line +//! interfaces that report progress to users. It comes with various +//! tools and utilities for formatting anything that indicates progress. +//! +//! Platform support: +//! +//! * Linux +//! * macOS +//! * Windows (colors require Windows 10) +//! +//! Best paired with other libraries in the family: +//! +//! * [console](https://docs.rs/console) +//! * [dialoguer](https://docs.rs/dialoguer) +//! +//! # Crate Contents +//! +//! * **Progress bars** +//! * [`ProgressBar`](struct.ProgressBar.html) for bars and spinners +//! * [`MultiProgress`](struct.MultiProgress.html) for multiple bars +//! * **Data Formatting** +//! * [`HumanBytes`](struct.HumanBytes.html) for formatting bytes +//! * [`DecimalBytes`](struct.DecimalBytes.html) for formatting bytes using SI prefixes +//! * [`BinaryBytes`](struct.BinaryBytes.html) for formatting bytes using ISO/IEC prefixes +//! * [`HumanDuration`](struct.HumanDuration.html) for formatting durations +//! * [`HumanCount`](struct.HumanCount.html) for formatting large counts +//! * [`HumanFloatCount`](struct.HumanFloatCount.html) for formatting large float counts +//! +//! # Progress Bars and Spinners +//! +//! indicatif comes with a `ProgressBar` type that supports both bounded +//! progress bar uses as well as unbounded "spinner" type progress reports. +//! Progress bars are `Sync` and `Send` objects which means that they are +//! internally locked and can be passed from thread to thread. +//! +//! Additionally a `MultiProgress` utility is provided that can manage +//! rendering multiple progress bars at once (eg: from multiple threads). +//! +//! To whet your appetite, this is what this can look like: +//! +//! +//! +//! Progress bars are manually advanced and by default draw to stderr. +//! When you are done, the progress bar can be finished either visibly +//! (eg: the progress bar stays on the screen) or cleared (the progress +//! bar will be removed). +//! +//! ```rust +//! use indicatif::ProgressBar; +//! +//! let bar = ProgressBar::new(1000); +//! for _ in 0..1000 { +//! bar.inc(1); +//! // ... +//! } +//! bar.finish(); +//! ``` +//! +//! General progress bar behaviors: +//! +//! * if a non terminal is detected the progress bar will be completely +//! hidden. This makes piping programs to logfiles make sense out of +//! the box. +//! * a progress bar only starts drawing when `set_message`, `inc`, `set_position` +//! or `tick` are called. In some situations you might have to call `tick` +//! once to draw it. +//! * progress bars should be explicitly finished to reset the rendering +//! for others. Either by also clearing them or by replacing them with +//! a new message / retaining the current message. +//! * the default template renders neither message nor prefix. +//! +//! # Iterators +//! +//! Similar to [tqdm](https://github.com/tqdm/tqdm), progress bars can be +//! associated with an iterator. For example: +//! +//! ```rust +//! use indicatif::ProgressIterator; +//! +//! for _ in (0..1000).progress() { +//! // ... +//! } +//! ``` +//! +//! See the [`ProgressIterator`](trait.ProgressIterator.html) trait for more +//! methods to configure the number of elements in the iterator or change +//! the progress bar style. Indicatif also has optional support for parallel +//! iterators with [Rayon](https://github.com/rayon-rs/rayon). In your +//! `Cargo.toml`, use the "rayon" feature: +//! +//! ```toml +//! [dependencies] +//! indicatif = {version = "*", features = ["rayon"]} +//! ``` +//! +//! And then use it like this: +//! +//! ```rust,ignore +//! # extern crate rayon; +//! use indicatif::ParallelProgressIterator; +//! use rayon::iter::{ParallelIterator, IntoParallelRefIterator}; +//! +//! let v: Vec<_> = (0..100000).collect(); +//! let v2: Vec<_> = v.par_iter().progress_count(v.len() as u64).map(|i| i + 1).collect(); +//! assert_eq!(v2[0], 1); +//! ``` +//! +//! Or if you'd like to customize the progress bar: +//! +//! ```rust,ignore +//! # extern crate rayon; +//! use indicatif::{ProgressBar, ParallelProgressIterator, ProgressStyle}; +//! use rayon::iter::{ParallelIterator, IntoParallelRefIterator}; +//! +//! // Alternatively, use `ProgressBar::new().with_style()` +//! let style = ProgressStyle::default_bar(); +//! let v: Vec<_> = (0..100000).collect(); +//! let v2: Vec<_> = v.par_iter().progress_with_style(style).map(|i| i + 1).collect(); +//! assert_eq!(v2[0], 1); +//! ``` +//! +//! # Templates +//! +//! Progress bars can be styled with simple format strings similar to the +//! ones in Rust itself. The format for a placeholder is `{key:options}` +//! where the `options` part is optional. If provided the format is this: +//! +//! ```text +//! <^> for an optional alignment specification (left, center and right respectively) +//! WIDTH an optional width as positive integer +//! ! an optional exclamation mark to enable truncation +//! .STYLE an optional dot separated style string +//! /STYLE an optional dot separated alternative style string +//! ``` +//! +//! For the style component see [`Style::from_dotted_str`](https://docs.rs/console/0.7.5/console/struct.Style.html#method.from_dotted_str) +//! for more information. Indicatif uses the `console` base crate for all +//! colorization and formatting options. +//! +//! Some examples for templates: +//! +//! ```text +//! [{elapsed_precise}] {bar:40.cyan/blue} {pos:>7}/{len:7} {msg} +//! ``` +//! +//! This sets a progress bar that is 40 characters wide and has cyan +//! as primary style color and blue as alternative style color. +//! Alternative styles are currently only used for progress bars. +//! +//! Example configuration: +//! +//! ```rust +//! # use indicatif::{ProgressBar, ProgressStyle}; +//! # let bar = ProgressBar::new(0); +//! bar.set_style(ProgressStyle::with_template("[{elapsed_precise}] {bar:40.cyan/blue} {pos:>7}/{len:7} {msg}") +//! .unwrap() +//! .progress_chars("##-")); +//! ``` +//! +//! The following keys exist: +//! +//! * `bar`: renders a progress bar. By default 20 characters wide. The +//! style string is used to color the elapsed part, the alternative +//! style is used for the bar that is yet to render. +//! * `wide_bar`: like `bar` but always fills the remaining space. It should not be used with +//! `wide_msg`. +//! * `spinner`: renders the spinner (current tick string). +//! * `prefix`: renders the prefix set on the progress bar. +//! * `msg`: renders the currently set message on the progress bar. +//! * `wide_msg`: like `msg` but always fills the remaining space and truncates. It should not be used +//! with `wide_bar`. +//! * `pos`: renders the current position of the bar as integer +//! * `human_pos`: renders the current position of the bar as an integer, with commas as the +//! thousands separator. +//! * `len`: renders the amount of work to be done as an integer +//! * `human_len`: renders the total length of the bar as an integer, with commas as the thousands +//! separator. +//! * `bytes`: renders the current position of the bar as bytes. +//! * `percent`: renders the current position of the bar as a percentage of the total length. +//! * `total_bytes`: renders the total length of the bar as bytes. +//! * `elapsed_precise`: renders the elapsed time as `HH:MM:SS`. +//! * `elapsed`: renders the elapsed time as `42s`, `1m` etc. +//! * `per_sec`: renders the speed in steps per second. +//! * `bytes_per_sec`: renders the speed in bytes per second. +//! * `binary_bytes_per_sec`: renders the speed in bytes per second using +//! power-of-two units, i.e. `MiB`, `KiB`, etc. +//! * `eta_precise`: the remaining time (like `elapsed_precise`). +//! * `eta`: the remaining time (like `elapsed`). +//! * `duration_precise`: the extrapolated total duration (like `elapsed_precise`). +//! * `duration`: the extrapolated total duration time (like `elapsed`). + +//! +//! The design of the progress bar can be altered with the integrated +//! template functionality. The template can be set by changing a +//! `ProgressStyle` and attaching it to the progress bar. +//! +//! # Human Readable Formatting +//! +//! There are some formatting wrappers for showing elapsed time and +//! file sizes for human users: +//! +//! ```rust +//! # use std::time::Duration; +//! use indicatif::{HumanBytes, HumanCount, HumanDuration, HumanFloatCount}; +//! +//! assert_eq!("3.00 MiB", HumanBytes(3*1024*1024).to_string()); +//! assert_eq!("8 seconds", HumanDuration(Duration::from_secs(8)).to_string()); +//! assert_eq!("33,857,009", HumanCount(33857009).to_string()); +//! assert_eq!("33,857,009.1235", HumanFloatCount(33857009.123456).to_string()); +//! ``` +//! +//! # Feature Flags +//! +//! * `rayon`: adds rayon support +//! * `improved_unicode`: adds improved unicode support (graphemes, better width calculation) + +#![cfg_attr(docsrs, feature(doc_cfg))] +#![warn(unreachable_pub)] + +mod draw_target; +mod format; +#[cfg(feature = "in_memory")] +mod in_memory; +mod iter; +mod multi; +mod progress_bar; +#[cfg(feature = "rayon")] +mod rayon; +mod state; +pub mod style; +mod term_like; + +pub use crate::draw_target::ProgressDrawTarget; +pub use crate::format::{ + BinaryBytes, DecimalBytes, FormattedDuration, HumanBytes, HumanCount, HumanDuration, + HumanFloatCount, +}; +#[cfg(feature = "in_memory")] +pub use crate::in_memory::InMemoryTerm; +pub use crate::iter::{ProgressBarIter, ProgressIterator}; +pub use crate::multi::{MultiProgress, MultiProgressAlignment}; +pub use crate::progress_bar::{ProgressBar, WeakProgressBar}; +#[cfg(feature = "rayon")] +pub use crate::rayon::ParallelProgressIterator; +pub use crate::state::{ProgressFinish, ProgressState}; +pub use crate::style::ProgressStyle; +pub use crate::term_like::TermLike; diff --git a/vendor/indicatif/src/multi.rs b/vendor/indicatif/src/multi.rs new file mode 100644 index 0000000..4409309 --- /dev/null +++ b/vendor/indicatif/src/multi.rs @@ -0,0 +1,688 @@ +use std::fmt::{Debug, Formatter}; +use std::io; +use std::sync::{Arc, RwLock}; +use std::thread::panicking; +#[cfg(not(target_arch = "wasm32"))] +use std::time::Instant; + +use crate::draw_target::{DrawState, DrawStateWrapper, LineAdjust, ProgressDrawTarget}; +use crate::progress_bar::ProgressBar; +#[cfg(target_arch = "wasm32")] +use instant::Instant; + +/// Manages multiple progress bars from different threads +#[derive(Debug, Clone)] +pub struct MultiProgress { + pub(crate) state: Arc>, +} + +impl Default for MultiProgress { + fn default() -> Self { + Self::with_draw_target(ProgressDrawTarget::stderr()) + } +} + +impl MultiProgress { + /// Creates a new multi progress object. + /// + /// Progress bars added to this object by default draw directly to stderr, and refresh + /// a maximum of 15 times a second. To change the refresh rate set the draw target to + /// one with a different refresh rate. + pub fn new() -> Self { + Self::default() + } + + /// Creates a new multi progress object with the given draw target. + pub fn with_draw_target(draw_target: ProgressDrawTarget) -> Self { + Self { + state: Arc::new(RwLock::new(MultiState::new(draw_target))), + } + } + + /// Sets a different draw target for the multiprogress bar. + pub fn set_draw_target(&self, target: ProgressDrawTarget) { + let mut state = self.state.write().unwrap(); + state.draw_target.disconnect(Instant::now()); + state.draw_target = target; + } + + /// Set whether we should try to move the cursor when possible instead of clearing lines. + /// + /// This can reduce flickering, but do not enable it if you intend to change the number of + /// progress bars. + pub fn set_move_cursor(&self, move_cursor: bool) { + self.state.write().unwrap().move_cursor = move_cursor; + } + + /// Set alignment flag + pub fn set_alignment(&self, alignment: MultiProgressAlignment) { + self.state.write().unwrap().alignment = alignment; + } + + /// Adds a progress bar. + /// + /// The progress bar added will have the draw target changed to a + /// remote draw target that is intercepted by the multi progress + /// object overriding custom `ProgressDrawTarget` settings. + /// + /// Adding a progress bar that is already a member of the `MultiProgress` + /// will have no effect. + pub fn add(&self, pb: ProgressBar) -> ProgressBar { + self.internalize(InsertLocation::End, pb) + } + + /// Inserts a progress bar. + /// + /// The progress bar inserted at position `index` will have the draw + /// target changed to a remote draw target that is intercepted by the + /// multi progress object overriding custom `ProgressDrawTarget` settings. + /// + /// If `index >= MultiProgressState::objects.len()`, the progress bar + /// is added to the end of the list. + /// + /// Inserting a progress bar that is already a member of the `MultiProgress` + /// will have no effect. + pub fn insert(&self, index: usize, pb: ProgressBar) -> ProgressBar { + self.internalize(InsertLocation::Index(index), pb) + } + + /// Inserts a progress bar from the back. + /// + /// The progress bar inserted at position `MultiProgressState::objects.len() - index` + /// will have the draw target changed to a remote draw target that is + /// intercepted by the multi progress object overriding custom + /// `ProgressDrawTarget` settings. + /// + /// If `index >= MultiProgressState::objects.len()`, the progress bar + /// is added to the start of the list. + /// + /// Inserting a progress bar that is already a member of the `MultiProgress` + /// will have no effect. + pub fn insert_from_back(&self, index: usize, pb: ProgressBar) -> ProgressBar { + self.internalize(InsertLocation::IndexFromBack(index), pb) + } + + /// Inserts a progress bar before an existing one. + /// + /// The progress bar added will have the draw target changed to a + /// remote draw target that is intercepted by the multi progress + /// object overriding custom `ProgressDrawTarget` settings. + /// + /// Inserting a progress bar that is already a member of the `MultiProgress` + /// will have no effect. + pub fn insert_before(&self, before: &ProgressBar, pb: ProgressBar) -> ProgressBar { + self.internalize(InsertLocation::Before(before.index().unwrap()), pb) + } + + /// Inserts a progress bar after an existing one. + /// + /// The progress bar added will have the draw target changed to a + /// remote draw target that is intercepted by the multi progress + /// object overriding custom `ProgressDrawTarget` settings. + /// + /// Inserting a progress bar that is already a member of the `MultiProgress` + /// will have no effect. + pub fn insert_after(&self, after: &ProgressBar, pb: ProgressBar) -> ProgressBar { + self.internalize(InsertLocation::After(after.index().unwrap()), pb) + } + + /// Removes a progress bar. + /// + /// The progress bar is removed only if it was previously inserted or added + /// by the methods `MultiProgress::insert` or `MultiProgress::add`. + /// If the passed progress bar does not satisfy the condition above, + /// the `remove` method does nothing. + pub fn remove(&self, pb: &ProgressBar) { + let mut state = pb.state(); + let idx = match &state.draw_target.remote() { + Some((state, idx)) => { + // Check that this progress bar is owned by the current MultiProgress. + assert!(Arc::ptr_eq(&self.state, state)); + *idx + } + _ => return, + }; + + state.draw_target = ProgressDrawTarget::hidden(); + self.state.write().unwrap().remove_idx(idx); + } + + fn internalize(&self, location: InsertLocation, pb: ProgressBar) -> ProgressBar { + let mut state = self.state.write().unwrap(); + let idx = state.insert(location); + drop(state); + + pb.set_draw_target(ProgressDrawTarget::new_remote(self.state.clone(), idx)); + pb + } + + /// Print a log line above all progress bars in the [`MultiProgress`] + /// + /// If the draw target is hidden (e.g. when standard output is not a terminal), `println()` + /// will not do anything. + pub fn println>(&self, msg: I) -> io::Result<()> { + let mut state = self.state.write().unwrap(); + state.println(msg, Instant::now()) + } + + /// Hide all progress bars temporarily, execute `f`, then redraw the [`MultiProgress`] + /// + /// Executes 'f' even if the draw target is hidden. + /// + /// Useful for external code that writes to the standard output. + /// + /// **Note:** The internal lock is held while `f` is executed. Other threads trying to print + /// anything on the progress bar will be blocked until `f` finishes. + /// Therefore, it is recommended to avoid long-running operations in `f`. + pub fn suspend R, R>(&self, f: F) -> R { + let mut state = self.state.write().unwrap(); + state.suspend(f, Instant::now()) + } + + pub fn clear(&self) -> io::Result<()> { + self.state.write().unwrap().clear(Instant::now()) + } + + pub fn is_hidden(&self) -> bool { + self.state.read().unwrap().is_hidden() + } +} + +#[derive(Debug)] +pub(crate) struct MultiState { + /// The collection of states corresponding to progress bars + members: Vec, + /// Set of removed bars, should have corresponding members in the `members` vector with a + /// `draw_state` of `None`. + free_set: Vec, + /// Indices to the `draw_states` to maintain correct visual order + ordering: Vec, + /// Target for draw operation for MultiProgress + draw_target: ProgressDrawTarget, + /// Whether or not to just move cursor instead of clearing lines + move_cursor: bool, + /// Controls how the multi progress is aligned if some of its progress bars get removed, default is `Top` + alignment: MultiProgressAlignment, + /// Lines to be drawn above everything else in the MultiProgress. These specifically come from + /// calling `ProgressBar::println` on a pb that is connected to a `MultiProgress`. + orphan_lines: Vec, + /// The count of currently visible zombie lines. + zombie_lines_count: usize, +} + +impl MultiState { + fn new(draw_target: ProgressDrawTarget) -> Self { + Self { + members: vec![], + free_set: vec![], + ordering: vec![], + draw_target, + move_cursor: false, + alignment: MultiProgressAlignment::default(), + orphan_lines: Vec::new(), + zombie_lines_count: 0, + } + } + + pub(crate) fn mark_zombie(&mut self, index: usize) { + let member = &mut self.members[index]; + + // If the zombie is the first visual bar then we can reap it right now instead of + // deferring it to the next draw. + if index != self.ordering.first().copied().unwrap() { + member.is_zombie = true; + return; + } + + let line_count = member + .draw_state + .as_ref() + .map(|d| d.lines.len()) + .unwrap_or_default(); + + // Track the total number of zombie lines on the screen + self.zombie_lines_count = self.zombie_lines_count.saturating_add(line_count); + + // Make `DrawTarget` forget about the zombie lines so that they aren't cleared on next draw. + self.draw_target + .adjust_last_line_count(LineAdjust::Keep(line_count)); + + self.remove_idx(index); + } + + pub(crate) fn draw( + &mut self, + mut force_draw: bool, + extra_lines: Option>, + now: Instant, + ) -> io::Result<()> { + if panicking() { + return Ok(()); + } + let width = self.width() as f64; + // Calculate real length based on terminal width + // This take in account linewrap from terminal + fn real_len(lines: &[String], width: f64) -> usize { + lines.iter().fold(0, |sum, val| { + sum + (console::measure_text_width(val) as f64 / width).ceil() as usize + }) + } + + // Assumption: if extra_lines is not None, then it has at least one line + debug_assert_eq!( + extra_lines.is_some(), + extra_lines.as_ref().map(Vec::len).unwrap_or_default() > 0 + ); + + let mut reap_indices = vec![]; + + // Reap all consecutive 'zombie' progress bars from head of the list. + let mut adjust = 0; + for &index in &self.ordering { + let member = &self.members[index]; + if !member.is_zombie { + break; + } + + let line_count = member + .draw_state + .as_ref() + .map(|d| real_len(&d.lines, width)) + .unwrap_or_default(); + // Track the total number of zombie lines on the screen. + self.zombie_lines_count += line_count; + + // Track the number of zombie lines that will be drawn by this call to draw. + adjust += line_count; + + reap_indices.push(index); + } + + // If this draw is due to a `println`, then we need to erase all the zombie lines. + // This is because `println` is supposed to appear above all other elements in the + // `MultiProgress`. + if extra_lines.is_some() { + self.draw_target + .adjust_last_line_count(LineAdjust::Clear(self.zombie_lines_count)); + self.zombie_lines_count = 0; + } + + let orphan_lines_count = real_len(&self.orphan_lines, width); + force_draw |= orphan_lines_count > 0; + let mut drawable = match self.draw_target.drawable(force_draw, now) { + Some(drawable) => drawable, + None => return Ok(()), + }; + + let mut draw_state = drawable.state(); + draw_state.orphan_lines_count = orphan_lines_count; + draw_state.alignment = self.alignment; + + if let Some(extra_lines) = &extra_lines { + draw_state.lines.extend_from_slice(extra_lines.as_slice()); + draw_state.orphan_lines_count += real_len(extra_lines, width); + } + + // Add lines from `ProgressBar::println` call. + draw_state.lines.append(&mut self.orphan_lines); + + for index in &self.ordering { + let member = &self.members[*index]; + if let Some(state) = &member.draw_state { + draw_state.lines.extend_from_slice(&state.lines[..]); + } + } + + drop(draw_state); + let drawable = drawable.draw(); + + for index in reap_indices { + self.remove_idx(index); + } + + // The zombie lines were drawn for the last time, so make `DrawTarget` forget about them + // so they aren't cleared on next draw. + if extra_lines.is_none() { + self.draw_target + .adjust_last_line_count(LineAdjust::Keep(adjust)); + } + + drawable + } + + pub(crate) fn println>(&mut self, msg: I, now: Instant) -> io::Result<()> { + let msg = msg.as_ref(); + + // If msg is "", make sure a line is still printed + let lines: Vec = match msg.is_empty() { + false => msg.lines().map(Into::into).collect(), + true => vec![String::new()], + }; + + self.draw(true, Some(lines), now) + } + + pub(crate) fn draw_state(&mut self, idx: usize) -> DrawStateWrapper<'_> { + let member = self.members.get_mut(idx).unwrap(); + // alignment is handled by the `MultiProgress`'s underlying draw target, so there is no + // point in propagating it here. + let state = member.draw_state.get_or_insert(DrawState { + move_cursor: self.move_cursor, + ..Default::default() + }); + + DrawStateWrapper::for_multi(state, &mut self.orphan_lines) + } + + pub(crate) fn is_hidden(&self) -> bool { + self.draw_target.is_hidden() + } + + pub(crate) fn suspend R, R>(&mut self, f: F, now: Instant) -> R { + self.clear(now).unwrap(); + let ret = f(); + self.draw(true, None, Instant::now()).unwrap(); + ret + } + + pub(crate) fn width(&self) -> u16 { + self.draw_target.width() + } + + fn insert(&mut self, location: InsertLocation) -> usize { + let idx = if let Some(idx) = self.free_set.pop() { + self.members[idx] = MultiStateMember::default(); + idx + } else { + self.members.push(MultiStateMember::default()); + self.members.len() - 1 + }; + + match location { + InsertLocation::End => self.ordering.push(idx), + InsertLocation::Index(pos) => { + let pos = Ord::min(pos, self.ordering.len()); + self.ordering.insert(pos, idx); + } + InsertLocation::IndexFromBack(pos) => { + let pos = self.ordering.len().saturating_sub(pos); + self.ordering.insert(pos, idx); + } + InsertLocation::After(after_idx) => { + let pos = self.ordering.iter().position(|i| *i == after_idx).unwrap(); + self.ordering.insert(pos + 1, idx); + } + InsertLocation::Before(before_idx) => { + let pos = self.ordering.iter().position(|i| *i == before_idx).unwrap(); + self.ordering.insert(pos, idx); + } + } + + assert_eq!( + self.len(), + self.ordering.len(), + "Draw state is inconsistent" + ); + + idx + } + + fn clear(&mut self, now: Instant) -> io::Result<()> { + match self.draw_target.drawable(true, now) { + Some(mut drawable) => { + // Make the clear operation also wipe out zombie lines + drawable.adjust_last_line_count(LineAdjust::Clear(self.zombie_lines_count)); + self.zombie_lines_count = 0; + drawable.clear() + } + None => Ok(()), + } + } + + fn remove_idx(&mut self, idx: usize) { + if self.free_set.contains(&idx) { + return; + } + + self.members[idx] = MultiStateMember::default(); + self.free_set.push(idx); + self.ordering.retain(|&x| x != idx); + + assert_eq!( + self.len(), + self.ordering.len(), + "Draw state is inconsistent" + ); + } + + fn len(&self) -> usize { + self.members.len() - self.free_set.len() + } +} + +#[derive(Default)] +struct MultiStateMember { + /// Draw state will be `None` for members that haven't been drawn before, or for entries that + /// correspond to something in the free set. + draw_state: Option, + /// Whether the corresponding progress bar (more precisely, `BarState`) has been dropped. + is_zombie: bool, +} + +impl Debug for MultiStateMember { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("MultiStateElement") + .field("draw_state", &self.draw_state) + .field("is_zombie", &self.is_zombie) + .finish_non_exhaustive() + } +} + +/// Vertical alignment of a multi progress. +/// +/// The alignment controls how the multi progress is aligned if some of its progress bars get removed. +/// E.g. `Top` alignment (default), when _progress bar 2_ is removed: +/// ```ignore +/// [0/100] progress bar 1 [0/100] progress bar 1 +/// [0/100] progress bar 2 => [0/100] progress bar 3 +/// [0/100] progress bar 3 +/// ``` +/// +/// `Bottom` alignment +/// ```ignore +/// [0/100] progress bar 1 +/// [0/100] progress bar 2 => [0/100] progress bar 1 +/// [0/100] progress bar 3 [0/100] progress bar 3 +/// ``` +#[derive(Debug, Copy, Clone)] +pub enum MultiProgressAlignment { + Top, + Bottom, +} + +impl Default for MultiProgressAlignment { + fn default() -> Self { + Self::Top + } +} + +enum InsertLocation { + End, + Index(usize), + IndexFromBack(usize), + After(usize), + Before(usize), +} + +#[cfg(test)] +mod tests { + use crate::{MultiProgress, ProgressBar, ProgressDrawTarget}; + + #[test] + fn late_pb_drop() { + let pb = ProgressBar::new(10); + let mpb = MultiProgress::new(); + // This clone call is required to trigger a now fixed bug. + // See for context + #[allow(clippy::redundant_clone)] + mpb.add(pb.clone()); + } + + #[test] + fn progress_bar_sync_send() { + let _: Box = Box::new(ProgressBar::new(1)); + let _: Box = Box::new(ProgressBar::new(1)); + let _: Box = Box::new(MultiProgress::new()); + let _: Box = Box::new(MultiProgress::new()); + } + + #[test] + fn multi_progress_hidden() { + let mpb = MultiProgress::with_draw_target(ProgressDrawTarget::hidden()); + let pb = mpb.add(ProgressBar::new(123)); + pb.finish(); + } + + #[test] + fn multi_progress_modifications() { + let mp = MultiProgress::new(); + let p0 = mp.add(ProgressBar::new(1)); + let p1 = mp.add(ProgressBar::new(1)); + let p2 = mp.add(ProgressBar::new(1)); + let p3 = mp.add(ProgressBar::new(1)); + mp.remove(&p2); + mp.remove(&p1); + let p4 = mp.insert(1, ProgressBar::new(1)); + + let state = mp.state.read().unwrap(); + // the removed place for p1 is reused + assert_eq!(state.members.len(), 4); + assert_eq!(state.len(), 3); + + // free_set may contain 1 or 2 + match state.free_set.last() { + Some(1) => { + assert_eq!(state.ordering, vec![0, 2, 3]); + assert!(state.members[1].draw_state.is_none()); + assert_eq!(p4.index().unwrap(), 2); + } + Some(2) => { + assert_eq!(state.ordering, vec![0, 1, 3]); + assert!(state.members[2].draw_state.is_none()); + assert_eq!(p4.index().unwrap(), 1); + } + _ => unreachable!(), + } + + assert_eq!(p0.index().unwrap(), 0); + assert_eq!(p1.index(), None); + assert_eq!(p2.index(), None); + assert_eq!(p3.index().unwrap(), 3); + } + + #[test] + fn multi_progress_insert_from_back() { + let mp = MultiProgress::new(); + let p0 = mp.add(ProgressBar::new(1)); + let p1 = mp.add(ProgressBar::new(1)); + let p2 = mp.add(ProgressBar::new(1)); + let p3 = mp.insert_from_back(1, ProgressBar::new(1)); + let p4 = mp.insert_from_back(10, ProgressBar::new(1)); + + let state = mp.state.read().unwrap(); + assert_eq!(state.ordering, vec![4, 0, 1, 3, 2]); + assert_eq!(p0.index().unwrap(), 0); + assert_eq!(p1.index().unwrap(), 1); + assert_eq!(p2.index().unwrap(), 2); + assert_eq!(p3.index().unwrap(), 3); + assert_eq!(p4.index().unwrap(), 4); + } + + #[test] + fn multi_progress_insert_after() { + let mp = MultiProgress::new(); + let p0 = mp.add(ProgressBar::new(1)); + let p1 = mp.add(ProgressBar::new(1)); + let p2 = mp.add(ProgressBar::new(1)); + let p3 = mp.insert_after(&p2, ProgressBar::new(1)); + let p4 = mp.insert_after(&p0, ProgressBar::new(1)); + + let state = mp.state.read().unwrap(); + assert_eq!(state.ordering, vec![0, 4, 1, 2, 3]); + assert_eq!(p0.index().unwrap(), 0); + assert_eq!(p1.index().unwrap(), 1); + assert_eq!(p2.index().unwrap(), 2); + assert_eq!(p3.index().unwrap(), 3); + assert_eq!(p4.index().unwrap(), 4); + } + + #[test] + fn multi_progress_insert_before() { + let mp = MultiProgress::new(); + let p0 = mp.add(ProgressBar::new(1)); + let p1 = mp.add(ProgressBar::new(1)); + let p2 = mp.add(ProgressBar::new(1)); + let p3 = mp.insert_before(&p0, ProgressBar::new(1)); + let p4 = mp.insert_before(&p2, ProgressBar::new(1)); + + let state = mp.state.read().unwrap(); + assert_eq!(state.ordering, vec![3, 0, 1, 4, 2]); + assert_eq!(p0.index().unwrap(), 0); + assert_eq!(p1.index().unwrap(), 1); + assert_eq!(p2.index().unwrap(), 2); + assert_eq!(p3.index().unwrap(), 3); + assert_eq!(p4.index().unwrap(), 4); + } + + #[test] + fn multi_progress_insert_before_and_after() { + let mp = MultiProgress::new(); + let p0 = mp.add(ProgressBar::new(1)); + let p1 = mp.add(ProgressBar::new(1)); + let p2 = mp.add(ProgressBar::new(1)); + let p3 = mp.insert_before(&p0, ProgressBar::new(1)); + let p4 = mp.insert_after(&p3, ProgressBar::new(1)); + let p5 = mp.insert_after(&p3, ProgressBar::new(1)); + let p6 = mp.insert_before(&p1, ProgressBar::new(1)); + + let state = mp.state.read().unwrap(); + assert_eq!(state.ordering, vec![3, 5, 4, 0, 6, 1, 2]); + assert_eq!(p0.index().unwrap(), 0); + assert_eq!(p1.index().unwrap(), 1); + assert_eq!(p2.index().unwrap(), 2); + assert_eq!(p3.index().unwrap(), 3); + assert_eq!(p4.index().unwrap(), 4); + assert_eq!(p5.index().unwrap(), 5); + assert_eq!(p6.index().unwrap(), 6); + } + + #[test] + fn multi_progress_multiple_remove() { + let mp = MultiProgress::new(); + let p0 = mp.add(ProgressBar::new(1)); + let p1 = mp.add(ProgressBar::new(1)); + // double remove beyond the first one have no effect + mp.remove(&p0); + mp.remove(&p0); + mp.remove(&p0); + + let state = mp.state.read().unwrap(); + // the removed place for p1 is reused + assert_eq!(state.members.len(), 2); + assert_eq!(state.free_set.len(), 1); + assert_eq!(state.len(), 1); + assert!(state.members[0].draw_state.is_none()); + assert_eq!(state.free_set.last(), Some(&0)); + + assert_eq!(state.ordering, vec![1]); + assert_eq!(p0.index(), None); + assert_eq!(p1.index().unwrap(), 1); + } + + #[test] + fn mp_no_crash_double_add() { + let mp = MultiProgress::new(); + let pb = mp.add(ProgressBar::new(10)); + mp.add(pb); + } +} diff --git a/vendor/indicatif/src/progress_bar.rs b/vendor/indicatif/src/progress_bar.rs new file mode 100644 index 0000000..938668e --- /dev/null +++ b/vendor/indicatif/src/progress_bar.rs @@ -0,0 +1,808 @@ +#[cfg(test)] +use portable_atomic::{AtomicBool, Ordering}; +use std::borrow::Cow; +use std::sync::{Arc, Condvar, Mutex, MutexGuard, Weak}; +use std::time::Duration; +#[cfg(not(target_arch = "wasm32"))] +use std::time::Instant; +use std::{fmt, io, thread}; + +#[cfg(target_arch = "wasm32")] +use instant::Instant; +#[cfg(test)] +use once_cell::sync::Lazy; + +use crate::draw_target::ProgressDrawTarget; +use crate::state::{AtomicPosition, BarState, ProgressFinish, Reset, TabExpandedString}; +use crate::style::ProgressStyle; +use crate::{ProgressBarIter, ProgressIterator, ProgressState}; + +/// A progress bar or spinner +/// +/// The progress bar is an [`Arc`] around its internal state. When the progress bar is cloned it +/// just increments the refcount (so the original and its clone share the same state). +#[derive(Clone)] +pub struct ProgressBar { + state: Arc>, + pos: Arc, + ticker: Arc>>, +} + +impl fmt::Debug for ProgressBar { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ProgressBar").finish() + } +} + +impl ProgressBar { + /// Creates a new progress bar with a given length + /// + /// This progress bar by default draws directly to stderr, and refreshes a maximum of 15 times + /// a second. To change the refresh rate, set the draw target to one with a different refresh + /// rate. + pub fn new(len: u64) -> Self { + Self::with_draw_target(Some(len), ProgressDrawTarget::stderr()) + } + + /// Creates a completely hidden progress bar + /// + /// This progress bar still responds to API changes but it does not have a length or render in + /// any way. + pub fn hidden() -> Self { + Self::with_draw_target(None, ProgressDrawTarget::hidden()) + } + + /// Creates a new progress bar with a given length and draw target + pub fn with_draw_target(len: Option, draw_target: ProgressDrawTarget) -> Self { + let pos = Arc::new(AtomicPosition::new()); + Self { + state: Arc::new(Mutex::new(BarState::new(len, draw_target, pos.clone()))), + pos, + ticker: Arc::new(Mutex::new(None)), + } + } + + /// Get a clone of the current progress bar style. + pub fn style(&self) -> ProgressStyle { + self.state().style.clone() + } + + /// A convenience builder-like function for a progress bar with a given style + pub fn with_style(self, style: ProgressStyle) -> Self { + self.set_style(style); + self + } + + /// A convenience builder-like function for a progress bar with a given tab width + pub fn with_tab_width(self, tab_width: usize) -> Self { + self.state().set_tab_width(tab_width); + self + } + + /// A convenience builder-like function for a progress bar with a given prefix + /// + /// For the prefix to be visible, the `{prefix}` placeholder must be present in the template + /// (see [`ProgressStyle`]). + pub fn with_prefix(self, prefix: impl Into>) -> Self { + let mut state = self.state(); + state.state.prefix = TabExpandedString::new(prefix.into(), state.tab_width); + drop(state); + self + } + + /// A convenience builder-like function for a progress bar with a given message + /// + /// For the message to be visible, the `{msg}` placeholder must be present in the template (see + /// [`ProgressStyle`]). + pub fn with_message(self, message: impl Into>) -> Self { + let mut state = self.state(); + state.state.message = TabExpandedString::new(message.into(), state.tab_width); + drop(state); + self + } + + /// A convenience builder-like function for a progress bar with a given position + pub fn with_position(self, pos: u64) -> Self { + self.state().state.set_pos(pos); + self + } + + /// A convenience builder-like function for a progress bar with a given elapsed time + pub fn with_elapsed(self, elapsed: Duration) -> Self { + self.state().state.started = Instant::now().checked_sub(elapsed).unwrap(); + self + } + + /// Sets the finish behavior for the progress bar + /// + /// This behavior is invoked when [`ProgressBar`] or + /// [`ProgressBarIter`] completes and + /// [`ProgressBar::is_finished()`] is false. + /// If you don't want the progress bar to be automatically finished then + /// call `on_finish(None)`. + /// + /// [`ProgressBar`]: crate::ProgressBar + /// [`ProgressBarIter`]: crate::ProgressBarIter + /// [`ProgressBar::is_finished()`]: crate::ProgressBar::is_finished + pub fn with_finish(self, finish: ProgressFinish) -> Self { + self.state().on_finish = finish; + self + } + + /// Creates a new spinner + /// + /// This spinner by default draws directly to stderr. This adds the default spinner style to it. + pub fn new_spinner() -> Self { + let rv = Self::with_draw_target(None, ProgressDrawTarget::stderr()); + rv.set_style(ProgressStyle::default_spinner()); + rv + } + + /// Overrides the stored style + /// + /// This does not redraw the bar. Call [`ProgressBar::tick()`] to force it. + pub fn set_style(&self, style: ProgressStyle) { + self.state().set_style(style); + } + + /// Sets the tab width (default: 8). All tabs will be expanded to this many spaces. + pub fn set_tab_width(&mut self, tab_width: usize) { + let mut state = self.state(); + state.set_tab_width(tab_width); + state.draw(true, Instant::now()).unwrap(); + } + + /// Spawns a background thread to tick the progress bar + /// + /// When this is enabled a background thread will regularly tick the progress bar in the given + /// interval. This is useful to advance progress bars that are very slow by themselves. + /// + /// When steady ticks are enabled, calling [`ProgressBar::tick()`] on a progress bar does not + /// have any effect. + pub fn enable_steady_tick(&self, interval: Duration) { + // The way we test for ticker termination is with a single static `AtomicBool`. Since cargo + // runs tests concurrently, we have a `TICKER_TEST` lock to make sure tests using ticker + // don't step on each other. This check catches attempts to use tickers in tests without + // acquiring the lock. + #[cfg(test)] + { + let guard = TICKER_TEST.try_lock(); + let lock_acquired = guard.is_ok(); + // Drop the guard before panicking to avoid poisoning the lock (which would cause other + // ticker tests to fail) + drop(guard); + if lock_acquired { + panic!("you must acquire the TICKER_TEST lock in your test to use this method"); + } + } + + if interval.is_zero() { + return; + } + + self.stop_and_replace_ticker(Some(interval)); + } + + /// Undoes [`ProgressBar::enable_steady_tick()`] + pub fn disable_steady_tick(&self) { + self.stop_and_replace_ticker(None); + } + + fn stop_and_replace_ticker(&self, interval: Option) { + let mut ticker_state = self.ticker.lock().unwrap(); + if let Some(ticker) = ticker_state.take() { + ticker.stop(); + } + + *ticker_state = interval.map(|interval| Ticker::new(interval, &self.state)); + } + + /// Manually ticks the spinner or progress bar + /// + /// This automatically happens on any other change to a progress bar. + pub fn tick(&self) { + self.tick_inner(Instant::now()); + } + + fn tick_inner(&self, now: Instant) { + // Only tick if a `Ticker` isn't installed + if self.ticker.lock().unwrap().is_none() { + self.state().tick(now); + } + } + + /// Advances the position of the progress bar by `delta` + pub fn inc(&self, delta: u64) { + self.pos.inc(delta); + let now = Instant::now(); + if self.pos.allow(now) { + self.tick_inner(now); + } + } + + /// A quick convenience check if the progress bar is hidden + pub fn is_hidden(&self) -> bool { + self.state().draw_target.is_hidden() + } + + /// Indicates that the progress bar finished + pub fn is_finished(&self) -> bool { + self.state().state.is_finished() + } + + /// Print a log line above the progress bar + /// + /// If the progress bar is hidden (e.g. when standard output is not a terminal), `println()` + /// will not do anything. If you want to write to the standard output in such cases as well, use + /// [`suspend`] instead. + /// + /// If the progress bar was added to a [`MultiProgress`], the log line will be + /// printed above all other progress bars. + /// + /// [`suspend`]: ProgressBar::suspend + /// [`MultiProgress`]: crate::MultiProgress + pub fn println>(&self, msg: I) { + self.state().println(Instant::now(), msg.as_ref()); + } + + /// Update the `ProgressBar`'s inner [`ProgressState`] + pub fn update(&self, f: impl FnOnce(&mut ProgressState)) { + self.state() + .update(Instant::now(), f, self.ticker.lock().unwrap().is_none()); + } + + /// Sets the position of the progress bar + pub fn set_position(&self, pos: u64) { + self.pos.set(pos); + let now = Instant::now(); + if self.pos.allow(now) { + self.tick_inner(now); + } + } + + /// Sets the length of the progress bar + pub fn set_length(&self, len: u64) { + self.state().set_length(Instant::now(), len); + } + + /// Increase the length of the progress bar + pub fn inc_length(&self, delta: u64) { + self.state().inc_length(Instant::now(), delta); + } + + /// Sets the current prefix of the progress bar + /// + /// For the prefix to be visible, the `{prefix}` placeholder must be present in the template + /// (see [`ProgressStyle`]). + pub fn set_prefix(&self, prefix: impl Into>) { + let mut state = self.state(); + state.state.prefix = TabExpandedString::new(prefix.into(), state.tab_width); + state.update_estimate_and_draw(Instant::now()); + } + + /// Sets the current message of the progress bar + /// + /// For the message to be visible, the `{msg}` placeholder must be present in the template (see + /// [`ProgressStyle`]). + pub fn set_message(&self, msg: impl Into>) { + let mut state = self.state(); + state.state.message = TabExpandedString::new(msg.into(), state.tab_width); + state.update_estimate_and_draw(Instant::now()); + } + + /// Creates a new weak reference to this `ProgressBar` + pub fn downgrade(&self) -> WeakProgressBar { + WeakProgressBar { + state: Arc::downgrade(&self.state), + pos: Arc::downgrade(&self.pos), + ticker: Arc::downgrade(&self.ticker), + } + } + + /// Resets the ETA calculation + /// + /// This can be useful if the progress bars made a large jump or was paused for a prolonged + /// time. + pub fn reset_eta(&self) { + self.state().reset(Instant::now(), Reset::Eta); + } + + /// Resets elapsed time and the ETA calculation + pub fn reset_elapsed(&self) { + self.state().reset(Instant::now(), Reset::Elapsed); + } + + /// Resets all of the progress bar state + pub fn reset(&self) { + self.state().reset(Instant::now(), Reset::All); + } + + /// Finishes the progress bar and leaves the current message + pub fn finish(&self) { + self.state() + .finish_using_style(Instant::now(), ProgressFinish::AndLeave); + } + + /// Finishes the progress bar and sets a message + /// + /// For the message to be visible, the `{msg}` placeholder must be present in the template (see + /// [`ProgressStyle`]). + pub fn finish_with_message(&self, msg: impl Into>) { + self.state() + .finish_using_style(Instant::now(), ProgressFinish::WithMessage(msg.into())); + } + + /// Finishes the progress bar and completely clears it + pub fn finish_and_clear(&self) { + self.state() + .finish_using_style(Instant::now(), ProgressFinish::AndClear); + } + + /// Finishes the progress bar and leaves the current message and progress + pub fn abandon(&self) { + self.state() + .finish_using_style(Instant::now(), ProgressFinish::Abandon); + } + + /// Finishes the progress bar and sets a message, and leaves the current progress + /// + /// For the message to be visible, the `{msg}` placeholder must be present in the template (see + /// [`ProgressStyle`]). + pub fn abandon_with_message(&self, msg: impl Into>) { + self.state().finish_using_style( + Instant::now(), + ProgressFinish::AbandonWithMessage(msg.into()), + ); + } + + /// Finishes the progress bar using the behavior stored in the [`ProgressStyle`] + /// + /// See [`ProgressBar::with_finish()`]. + pub fn finish_using_style(&self) { + let mut state = self.state(); + let finish = state.on_finish.clone(); + state.finish_using_style(Instant::now(), finish); + } + + /// Sets a different draw target for the progress bar + /// + /// This can be used to draw the progress bar to stderr (this is the default): + /// + /// ```rust,no_run + /// # use indicatif::{ProgressBar, ProgressDrawTarget}; + /// let pb = ProgressBar::new(100); + /// pb.set_draw_target(ProgressDrawTarget::stderr()); + /// ``` + /// + /// **Note:** Calling this method on a [`ProgressBar`] linked with a [`MultiProgress`] (after + /// running [`MultiProgress::add`]) will unlink this progress bar. If you don't want this + /// behavior, call [`MultiProgress::set_draw_target`] instead. + /// + /// [`MultiProgress`]: crate::MultiProgress + /// [`MultiProgress::add`]: crate::MultiProgress::add + /// [`MultiProgress::set_draw_target`]: crate::MultiProgress::set_draw_target + pub fn set_draw_target(&self, target: ProgressDrawTarget) { + let mut state = self.state(); + state.draw_target.disconnect(Instant::now()); + state.draw_target = target; + } + + /// Hide the progress bar temporarily, execute `f`, then redraw the progress bar + /// + /// Useful for external code that writes to the standard output. + /// + /// If the progress bar was added to a MultiProgress, it will suspend the entire MultiProgress + /// + /// **Note:** The internal lock is held while `f` is executed. Other threads trying to print + /// anything on the progress bar will be blocked until `f` finishes. + /// Therefore, it is recommended to avoid long-running operations in `f`. + /// + /// ```rust,no_run + /// # use indicatif::ProgressBar; + /// let mut pb = ProgressBar::new(3); + /// pb.suspend(|| { + /// println!("Log message"); + /// }) + /// ``` + pub fn suspend R, R>(&self, f: F) -> R { + self.state().suspend(Instant::now(), f) + } + + /// Wraps an [`Iterator`] with the progress bar + /// + /// ```rust,no_run + /// # use indicatif::ProgressBar; + /// let v = vec![1, 2, 3]; + /// let pb = ProgressBar::new(3); + /// for item in pb.wrap_iter(v.iter()) { + /// // ... + /// } + /// ``` + pub fn wrap_iter(&self, it: It) -> ProgressBarIter { + it.progress_with(self.clone()) + } + + /// Wraps an [`io::Read`] with the progress bar + /// + /// ```rust,no_run + /// # use std::fs::File; + /// # use std::io; + /// # use indicatif::ProgressBar; + /// # fn test () -> io::Result<()> { + /// let source = File::open("work.txt")?; + /// let mut target = File::create("done.txt")?; + /// let pb = ProgressBar::new(source.metadata()?.len()); + /// io::copy(&mut pb.wrap_read(source), &mut target); + /// # Ok(()) + /// # } + /// ``` + pub fn wrap_read(&self, read: R) -> ProgressBarIter { + ProgressBarIter { + progress: self.clone(), + it: read, + } + } + + /// Wraps an [`io::Write`] with the progress bar + /// + /// ```rust,no_run + /// # use std::fs::File; + /// # use std::io; + /// # use indicatif::ProgressBar; + /// # fn test () -> io::Result<()> { + /// let mut source = File::open("work.txt")?; + /// let target = File::create("done.txt")?; + /// let pb = ProgressBar::new(source.metadata()?.len()); + /// io::copy(&mut source, &mut pb.wrap_write(target)); + /// # Ok(()) + /// # } + /// ``` + pub fn wrap_write(&self, write: W) -> ProgressBarIter { + ProgressBarIter { + progress: self.clone(), + it: write, + } + } + + #[cfg(feature = "tokio")] + #[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] + /// Wraps an [`tokio::io::AsyncWrite`] with the progress bar + /// + /// ```rust,no_run + /// # use tokio::fs::File; + /// # use tokio::io; + /// # use indicatif::ProgressBar; + /// # async fn test() -> io::Result<()> { + /// let mut source = File::open("work.txt").await?; + /// let mut target = File::open("done.txt").await?; + /// let pb = ProgressBar::new(source.metadata().await?.len()); + /// io::copy(&mut source, &mut pb.wrap_async_write(target)).await?; + /// # Ok(()) + /// # } + /// ``` + pub fn wrap_async_write( + &self, + write: W, + ) -> ProgressBarIter { + ProgressBarIter { + progress: self.clone(), + it: write, + } + } + + #[cfg(feature = "tokio")] + #[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] + /// Wraps an [`tokio::io::AsyncRead`] with the progress bar + /// + /// ```rust,no_run + /// # use tokio::fs::File; + /// # use tokio::io; + /// # use indicatif::ProgressBar; + /// # async fn test() -> io::Result<()> { + /// let mut source = File::open("work.txt").await?; + /// let mut target = File::open("done.txt").await?; + /// let pb = ProgressBar::new(source.metadata().await?.len()); + /// io::copy(&mut pb.wrap_async_read(source), &mut target).await?; + /// # Ok(()) + /// # } + /// ``` + pub fn wrap_async_read(&self, read: R) -> ProgressBarIter { + ProgressBarIter { + progress: self.clone(), + it: read, + } + } + + /// Wraps a [`futures::Stream`](https://docs.rs/futures/0.3/futures/stream/trait.StreamExt.html) with the progress bar + /// + /// ``` + /// # use indicatif::ProgressBar; + /// # futures::executor::block_on(async { + /// use futures::stream::{self, StreamExt}; + /// let pb = ProgressBar::new(10); + /// let mut stream = pb.wrap_stream(stream::iter('a'..='z')); + /// + /// assert_eq!(stream.next().await, Some('a')); + /// assert_eq!(stream.count().await, 25); + /// # }); // block_on + /// ``` + #[cfg(feature = "futures")] + #[cfg_attr(docsrs, doc(cfg(feature = "futures")))] + pub fn wrap_stream(&self, stream: S) -> ProgressBarIter { + ProgressBarIter { + progress: self.clone(), + it: stream, + } + } + + /// Returns the current position + pub fn position(&self) -> u64 { + self.state().state.pos() + } + + /// Returns the current length + pub fn length(&self) -> Option { + self.state().state.len() + } + + /// Returns the current ETA + pub fn eta(&self) -> Duration { + self.state().state.eta() + } + + /// Returns the current rate of progress + pub fn per_sec(&self) -> f64 { + self.state().state.per_sec() + } + + /// Returns the current expected duration + pub fn duration(&self) -> Duration { + self.state().state.duration() + } + + /// Returns the current elapsed time + pub fn elapsed(&self) -> Duration { + self.state().state.elapsed() + } + + /// Index in the `MultiState` + pub(crate) fn index(&self) -> Option { + self.state().draw_target.remote().map(|(_, idx)| idx) + } + + /// Current message + pub fn message(&self) -> String { + self.state().state.message.expanded().to_string() + } + + /// Current prefix + pub fn prefix(&self) -> String { + self.state().state.prefix.expanded().to_string() + } + + #[inline] + pub(crate) fn state(&self) -> MutexGuard<'_, BarState> { + self.state.lock().unwrap() + } +} + +/// A weak reference to a `ProgressBar`. +/// +/// Useful for creating custom steady tick implementations +#[derive(Clone, Default)] +pub struct WeakProgressBar { + state: Weak>, + pos: Weak, + ticker: Weak>>, +} + +impl WeakProgressBar { + /// Create a new `WeakProgressBar` that returns `None` when [`upgrade`] is called. + /// + /// [`upgrade`]: WeakProgressBar::upgrade + pub fn new() -> Self { + Self::default() + } + + /// Attempts to upgrade the Weak pointer to a [`ProgressBar`], delaying dropping of the inner + /// value if successful. Returns `None` if the inner value has since been dropped. + /// + /// [`ProgressBar`]: struct.ProgressBar.html + pub fn upgrade(&self) -> Option { + let state = self.state.upgrade()?; + let pos = self.pos.upgrade()?; + let ticker = self.ticker.upgrade()?; + Some(ProgressBar { state, pos, ticker }) + } +} + +pub(crate) struct Ticker { + stopping: Arc<(Mutex, Condvar)>, + join_handle: Option>, +} + +impl Drop for Ticker { + fn drop(&mut self) { + self.stop(); + self.join_handle.take().map(|handle| handle.join()); + } +} + +#[cfg(test)] +static TICKER_RUNNING: AtomicBool = AtomicBool::new(false); + +impl Ticker { + pub(crate) fn new(interval: Duration, bar_state: &Arc>) -> Self { + debug_assert!(!interval.is_zero()); + + // A `Mutex` is used as a flag to indicate whether the ticker was requested to stop. + // The `Condvar` is used a notification mechanism: when the ticker is dropped, we notify + // the thread and interrupt the ticker wait. + #[allow(clippy::mutex_atomic)] + let stopping = Arc::new((Mutex::new(false), Condvar::new())); + let control = TickerControl { + stopping: stopping.clone(), + state: Arc::downgrade(bar_state), + }; + + let join_handle = thread::spawn(move || control.run(interval)); + Self { + stopping, + join_handle: Some(join_handle), + } + } + + pub(crate) fn stop(&self) { + *self.stopping.0.lock().unwrap() = true; + self.stopping.1.notify_one(); + } +} + +struct TickerControl { + stopping: Arc<(Mutex, Condvar)>, + state: Weak>, +} + +impl TickerControl { + fn run(&self, interval: Duration) { + #[cfg(test)] + TICKER_RUNNING.store(true, Ordering::SeqCst); + + while let Some(arc) = self.state.upgrade() { + let mut state = arc.lock().unwrap(); + if state.state.is_finished() { + break; + } + + state.tick(Instant::now()); + + drop(state); // Don't forget to drop the lock before sleeping + drop(arc); // Also need to drop Arc otherwise BarState won't be dropped + + // Wait for `interval` but return early if we are notified to stop + let (_, result) = self + .stopping + .1 + .wait_timeout_while(self.stopping.0.lock().unwrap(), interval, |stopped| { + !*stopped + }) + .unwrap(); + + // If the wait didn't time out, it means we were notified to stop + if !result.timed_out() { + break; + } + } + + #[cfg(test)] + TICKER_RUNNING.store(false, Ordering::SeqCst); + } +} + +// Tests using the global TICKER_RUNNING flag need to be serialized +#[cfg(test)] +pub(crate) static TICKER_TEST: Lazy> = Lazy::new(Mutex::default); + +#[cfg(test)] +mod tests { + use super::*; + + #[allow(clippy::float_cmp)] + #[test] + fn test_pbar_zero() { + let pb = ProgressBar::new(0); + assert_eq!(pb.state().state.fraction(), 1.0); + } + + #[allow(clippy::float_cmp)] + #[test] + fn test_pbar_maxu64() { + let pb = ProgressBar::new(!0); + assert_eq!(pb.state().state.fraction(), 0.0); + } + + #[test] + fn test_pbar_overflow() { + let pb = ProgressBar::new(1); + pb.set_draw_target(ProgressDrawTarget::hidden()); + pb.inc(2); + pb.finish(); + } + + #[test] + fn test_get_position() { + let pb = ProgressBar::new(1); + pb.set_draw_target(ProgressDrawTarget::hidden()); + pb.inc(2); + let pos = pb.position(); + assert_eq!(pos, 2); + } + + #[test] + fn test_weak_pb() { + let pb = ProgressBar::new(0); + let weak = pb.downgrade(); + assert!(weak.upgrade().is_some()); + ::std::mem::drop(pb); + assert!(weak.upgrade().is_none()); + } + + #[test] + fn it_can_wrap_a_reader() { + let bytes = &b"I am an implementation of io::Read"[..]; + let pb = ProgressBar::new(bytes.len() as u64); + let mut reader = pb.wrap_read(bytes); + let mut writer = Vec::new(); + io::copy(&mut reader, &mut writer).unwrap(); + assert_eq!(writer, bytes); + } + + #[test] + fn it_can_wrap_a_writer() { + let bytes = b"implementation of io::Read"; + let mut reader = &bytes[..]; + let pb = ProgressBar::new(bytes.len() as u64); + let writer = Vec::new(); + let mut writer = pb.wrap_write(writer); + io::copy(&mut reader, &mut writer).unwrap(); + assert_eq!(writer.it, bytes); + } + + #[test] + fn ticker_thread_terminates_on_drop() { + let _guard = TICKER_TEST.lock().unwrap(); + assert!(!TICKER_RUNNING.load(Ordering::SeqCst)); + + let pb = ProgressBar::new_spinner(); + pb.enable_steady_tick(Duration::from_millis(50)); + + // Give the thread time to start up + thread::sleep(Duration::from_millis(250)); + + assert!(TICKER_RUNNING.load(Ordering::SeqCst)); + + drop(pb); + assert!(!TICKER_RUNNING.load(Ordering::SeqCst)); + } + + #[test] + fn ticker_thread_terminates_on_drop_2() { + let _guard = TICKER_TEST.lock().unwrap(); + assert!(!TICKER_RUNNING.load(Ordering::SeqCst)); + + let pb = ProgressBar::new_spinner(); + pb.enable_steady_tick(Duration::from_millis(50)); + let pb2 = pb.clone(); + + // Give the thread time to start up + thread::sleep(Duration::from_millis(250)); + + assert!(TICKER_RUNNING.load(Ordering::SeqCst)); + + drop(pb); + assert!(TICKER_RUNNING.load(Ordering::SeqCst)); + + drop(pb2); + assert!(!TICKER_RUNNING.load(Ordering::SeqCst)); + } +} diff --git a/vendor/indicatif/src/rayon.rs b/vendor/indicatif/src/rayon.rs new file mode 100644 index 0000000..1c8e844 --- /dev/null +++ b/vendor/indicatif/src/rayon.rs @@ -0,0 +1,235 @@ +use rayon::iter::plumbing::{Consumer, Folder, Producer, ProducerCallback, UnindexedConsumer}; +use rayon::iter::{IndexedParallelIterator, ParallelIterator}; + +use crate::{ProgressBar, ProgressBarIter}; + +/// Wraps a Rayon parallel iterator. +/// +/// See [`ProgressIterator`](trait.ProgressIterator.html) for method +/// documentation. +#[cfg_attr(docsrs, doc(cfg(feature = "rayon")))] +pub trait ParallelProgressIterator +where + Self: Sized + ParallelIterator, +{ + /// Wrap an iterator with a custom progress bar. + fn progress_with(self, progress: ProgressBar) -> ProgressBarIter; + + /// Wrap an iterator with an explicit element count. + fn progress_count(self, len: u64) -> ProgressBarIter { + self.progress_with(ProgressBar::new(len)) + } + + fn progress(self) -> ProgressBarIter + where + Self: IndexedParallelIterator, + { + let len = u64::try_from(self.len()).unwrap(); + self.progress_count(len) + } + + /// Wrap an iterator with a progress bar and style it. + fn progress_with_style(self, style: crate::ProgressStyle) -> ProgressBarIter + where + Self: IndexedParallelIterator, + { + let len = u64::try_from(self.len()).unwrap(); + let bar = ProgressBar::new(len).with_style(style); + self.progress_with(bar) + } +} + +impl> ParallelProgressIterator for T { + fn progress_with(self, progress: ProgressBar) -> ProgressBarIter { + ProgressBarIter { it: self, progress } + } +} + +impl> IndexedParallelIterator for ProgressBarIter { + fn len(&self) -> usize { + self.it.len() + } + + fn drive>(self, consumer: C) -> >::Result { + let consumer = ProgressConsumer::new(consumer, self.progress); + self.it.drive(consumer) + } + + fn with_producer>( + self, + callback: CB, + ) -> >::Output { + return self.it.with_producer(Callback { + callback, + progress: self.progress, + }); + + struct Callback { + callback: CB, + progress: ProgressBar, + } + + impl> ProducerCallback for Callback { + type Output = CB::Output; + + fn callback

(self, base: P) -> CB::Output + where + P: Producer, + { + let producer = ProgressProducer { + base, + progress: self.progress, + }; + self.callback.callback(producer) + } + } + } +} + +struct ProgressProducer { + base: T, + progress: ProgressBar, +} + +impl> Producer for ProgressProducer

{ + type Item = T; + type IntoIter = ProgressBarIter; + + fn into_iter(self) -> Self::IntoIter { + ProgressBarIter { + it: self.base.into_iter(), + progress: self.progress, + } + } + + fn min_len(&self) -> usize { + self.base.min_len() + } + + fn max_len(&self) -> usize { + self.base.max_len() + } + + fn split_at(self, index: usize) -> (Self, Self) { + let (left, right) = self.base.split_at(index); + ( + ProgressProducer { + base: left, + progress: self.progress.clone(), + }, + ProgressProducer { + base: right, + progress: self.progress, + }, + ) + } +} + +struct ProgressConsumer { + base: C, + progress: ProgressBar, +} + +impl ProgressConsumer { + fn new(base: C, progress: ProgressBar) -> Self { + ProgressConsumer { base, progress } + } +} + +impl> Consumer for ProgressConsumer { + type Folder = ProgressFolder; + type Reducer = C::Reducer; + type Result = C::Result; + + fn split_at(self, index: usize) -> (Self, Self, Self::Reducer) { + let (left, right, reducer) = self.base.split_at(index); + ( + ProgressConsumer::new(left, self.progress.clone()), + ProgressConsumer::new(right, self.progress), + reducer, + ) + } + + fn into_folder(self) -> Self::Folder { + ProgressFolder { + base: self.base.into_folder(), + progress: self.progress, + } + } + + fn full(&self) -> bool { + self.base.full() + } +} + +impl> UnindexedConsumer for ProgressConsumer { + fn split_off_left(&self) -> Self { + ProgressConsumer::new(self.base.split_off_left(), self.progress.clone()) + } + + fn to_reducer(&self) -> Self::Reducer { + self.base.to_reducer() + } +} + +struct ProgressFolder { + base: C, + progress: ProgressBar, +} + +impl> Folder for ProgressFolder { + type Result = C::Result; + + fn consume(self, item: T) -> Self { + self.progress.inc(1); + ProgressFolder { + base: self.base.consume(item), + progress: self.progress, + } + } + + fn complete(self) -> C::Result { + self.base.complete() + } + + fn full(&self) -> bool { + self.base.full() + } +} + +impl> ParallelIterator for ProgressBarIter { + type Item = S; + + fn drive_unindexed>(self, consumer: C) -> C::Result { + let consumer1 = ProgressConsumer::new(consumer, self.progress.clone()); + self.it.drive_unindexed(consumer1) + } +} + +#[cfg(test)] +mod test { + use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; + + use crate::{ParallelProgressIterator, ProgressBar, ProgressBarIter, ProgressStyle}; + + #[test] + fn it_can_wrap_a_parallel_iterator() { + let v = vec![1, 2, 3]; + fn wrap<'a, T: ParallelIterator>(it: ProgressBarIter) { + assert_eq!(it.map(|x| x * 2).collect::>(), vec![2, 4, 6]); + } + + wrap(v.par_iter().progress_count(3)); + wrap({ + let pb = ProgressBar::new(v.len() as u64); + v.par_iter().progress_with(pb) + }); + + wrap({ + let style = ProgressStyle::default_bar() + .template("{wide_bar:.red} {percent}/100%") + .unwrap(); + v.par_iter().progress_with_style(style) + }); + } +} diff --git a/vendor/indicatif/src/state.rs b/vendor/indicatif/src/state.rs new file mode 100644 index 0000000..3bcdc44 --- /dev/null +++ b/vendor/indicatif/src/state.rs @@ -0,0 +1,798 @@ +use std::borrow::Cow; +use std::io; +use std::sync::Arc; +use std::time::Duration; +#[cfg(not(target_arch = "wasm32"))] +use std::time::Instant; + +#[cfg(target_arch = "wasm32")] +use instant::Instant; +use portable_atomic::{AtomicU64, AtomicU8, Ordering}; + +use crate::draw_target::ProgressDrawTarget; +use crate::style::ProgressStyle; + +pub(crate) struct BarState { + pub(crate) draw_target: ProgressDrawTarget, + pub(crate) on_finish: ProgressFinish, + pub(crate) style: ProgressStyle, + pub(crate) state: ProgressState, + pub(crate) tab_width: usize, +} + +impl BarState { + pub(crate) fn new( + len: Option, + draw_target: ProgressDrawTarget, + pos: Arc, + ) -> Self { + Self { + draw_target, + on_finish: ProgressFinish::default(), + style: ProgressStyle::default_bar(), + state: ProgressState::new(len, pos), + tab_width: DEFAULT_TAB_WIDTH, + } + } + + /// Finishes the progress bar using the [`ProgressFinish`] behavior stored + /// in the [`ProgressStyle`]. + pub(crate) fn finish_using_style(&mut self, now: Instant, finish: ProgressFinish) { + self.state.status = Status::DoneVisible; + match finish { + ProgressFinish::AndLeave => { + if let Some(len) = self.state.len { + self.state.pos.set(len); + } + } + ProgressFinish::WithMessage(msg) => { + if let Some(len) = self.state.len { + self.state.pos.set(len); + } + self.state.message = TabExpandedString::new(msg, self.tab_width); + } + ProgressFinish::AndClear => { + if let Some(len) = self.state.len { + self.state.pos.set(len); + } + self.state.status = Status::DoneHidden; + } + ProgressFinish::Abandon => {} + ProgressFinish::AbandonWithMessage(msg) => { + self.state.message = TabExpandedString::new(msg, self.tab_width); + } + } + + // There's no need to update the estimate here; once the `status` is no longer + // `InProgress`, we will use the length and elapsed time to estimate. + let _ = self.draw(true, now); + } + + pub(crate) fn reset(&mut self, now: Instant, mode: Reset) { + // Always reset the estimator; this is the only reset that will occur if mode is + // `Reset::Eta`. + self.state.est.reset(now); + + if let Reset::Elapsed | Reset::All = mode { + self.state.started = now; + } + + if let Reset::All = mode { + self.state.pos.reset(now); + self.state.status = Status::InProgress; + + for tracker in self.style.format_map.values_mut() { + tracker.reset(&self.state, now); + } + + let _ = self.draw(false, now); + } + } + + pub(crate) fn update(&mut self, now: Instant, f: impl FnOnce(&mut ProgressState), tick: bool) { + f(&mut self.state); + if tick { + self.tick(now); + } + } + + pub(crate) fn set_length(&mut self, now: Instant, len: u64) { + self.state.len = Some(len); + self.update_estimate_and_draw(now); + } + + pub(crate) fn inc_length(&mut self, now: Instant, delta: u64) { + if let Some(len) = self.state.len { + self.state.len = Some(len.saturating_add(delta)); + } + self.update_estimate_and_draw(now); + } + + pub(crate) fn set_tab_width(&mut self, tab_width: usize) { + self.tab_width = tab_width; + self.state.message.set_tab_width(tab_width); + self.state.prefix.set_tab_width(tab_width); + self.style.set_tab_width(tab_width); + } + + pub(crate) fn set_style(&mut self, style: ProgressStyle) { + self.style = style; + self.style.set_tab_width(self.tab_width); + } + + pub(crate) fn tick(&mut self, now: Instant) { + self.state.tick = self.state.tick.saturating_add(1); + self.update_estimate_and_draw(now); + } + + pub(crate) fn update_estimate_and_draw(&mut self, now: Instant) { + let pos = self.state.pos.pos.load(Ordering::Relaxed); + self.state.est.record(pos, now); + + for tracker in self.style.format_map.values_mut() { + tracker.tick(&self.state, now); + } + + let _ = self.draw(false, now); + } + + pub(crate) fn println(&mut self, now: Instant, msg: &str) { + let width = self.draw_target.width(); + let mut drawable = match self.draw_target.drawable(true, now) { + Some(drawable) => drawable, + None => return, + }; + + let mut draw_state = drawable.state(); + let lines: Vec = msg.lines().map(Into::into).collect(); + // Empty msg should trigger newline as we are in println + if lines.is_empty() { + draw_state.lines.push(String::new()); + } else { + draw_state.lines.extend(lines); + } + draw_state.orphan_lines_count = draw_state.lines.len(); + if !matches!(self.state.status, Status::DoneHidden) { + self.style + .format_state(&self.state, &mut draw_state.lines, width); + } + + drop(draw_state); + let _ = drawable.draw(); + } + + pub(crate) fn suspend R, R>(&mut self, now: Instant, f: F) -> R { + if let Some((state, _)) = self.draw_target.remote() { + return state.write().unwrap().suspend(f, now); + } + + if let Some(drawable) = self.draw_target.drawable(true, now) { + let _ = drawable.clear(); + } + + let ret = f(); + let _ = self.draw(true, Instant::now()); + ret + } + + pub(crate) fn draw(&mut self, mut force_draw: bool, now: Instant) -> io::Result<()> { + let width = self.draw_target.width(); + + // `|= self.is_finished()` should not be needed here, but we used to always draw for + // finished progress bars, so it's kept as to not cause compatibility issues in weird cases. + force_draw |= self.state.is_finished(); + let mut drawable = match self.draw_target.drawable(force_draw, now) { + Some(drawable) => drawable, + None => return Ok(()), + }; + + let mut draw_state = drawable.state(); + + if !matches!(self.state.status, Status::DoneHidden) { + self.style + .format_state(&self.state, &mut draw_state.lines, width); + } + + drop(draw_state); + drawable.draw() + } +} + +impl Drop for BarState { + fn drop(&mut self) { + // Progress bar is already finished. Do not need to do anything other than notify + // the `MultiProgress` that we're now a zombie. + if self.state.is_finished() { + self.draw_target.mark_zombie(); + return; + } + + self.finish_using_style(Instant::now(), self.on_finish.clone()); + + // Notify the `MultiProgress` that we're now a zombie. + self.draw_target.mark_zombie(); + } +} + +pub(crate) enum Reset { + Eta, + Elapsed, + All, +} + +/// The state of a progress bar at a moment in time. +#[non_exhaustive] +pub struct ProgressState { + pos: Arc, + len: Option, + pub(crate) tick: u64, + pub(crate) started: Instant, + status: Status, + est: Estimator, + pub(crate) message: TabExpandedString, + pub(crate) prefix: TabExpandedString, +} + +impl ProgressState { + pub(crate) fn new(len: Option, pos: Arc) -> Self { + let now = Instant::now(); + Self { + pos, + len, + tick: 0, + status: Status::InProgress, + started: now, + est: Estimator::new(now), + message: TabExpandedString::NoTabs("".into()), + prefix: TabExpandedString::NoTabs("".into()), + } + } + + /// Indicates that the progress bar finished. + pub fn is_finished(&self) -> bool { + match self.status { + Status::InProgress => false, + Status::DoneVisible => true, + Status::DoneHidden => true, + } + } + + /// Returns the completion as a floating-point number between 0 and 1 + pub fn fraction(&self) -> f32 { + let pos = self.pos.pos.load(Ordering::Relaxed); + let pct = match (pos, self.len) { + (_, None) => 0.0, + (_, Some(0)) => 1.0, + (0, _) => 0.0, + (pos, Some(len)) => pos as f32 / len as f32, + }; + pct.clamp(0.0, 1.0) + } + + /// The expected ETA + pub fn eta(&self) -> Duration { + if self.is_finished() { + return Duration::new(0, 0); + } + + let len = match self.len { + Some(len) => len, + None => return Duration::new(0, 0), + }; + + let pos = self.pos.pos.load(Ordering::Relaxed); + + let sps = self.est.steps_per_second(Instant::now()); + + // Infinite duration should only ever happen at the beginning, so in this case it's okay to + // just show an ETA of 0 until progress starts to occur. + if sps == 0.0 { + return Duration::new(0, 0); + } + + secs_to_duration(len.saturating_sub(pos) as f64 / sps) + } + + /// The expected total duration (that is, elapsed time + expected ETA) + pub fn duration(&self) -> Duration { + if self.len.is_none() || self.is_finished() { + return Duration::new(0, 0); + } + self.started.elapsed().saturating_add(self.eta()) + } + + /// The number of steps per second + pub fn per_sec(&self) -> f64 { + if let Status::InProgress = self.status { + self.est.steps_per_second(Instant::now()) + } else { + let len = self.len.unwrap_or_else(|| self.pos()); + len as f64 / self.started.elapsed().as_secs_f64() + } + } + + pub fn elapsed(&self) -> Duration { + self.started.elapsed() + } + + pub fn pos(&self) -> u64 { + self.pos.pos.load(Ordering::Relaxed) + } + + pub fn set_pos(&mut self, pos: u64) { + self.pos.set(pos); + } + + #[allow(clippy::len_without_is_empty)] + pub fn len(&self) -> Option { + self.len + } + + pub fn set_len(&mut self, len: u64) { + self.len = Some(len); + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub(crate) enum TabExpandedString { + NoTabs(Cow<'static, str>), + WithTabs { + original: Cow<'static, str>, + expanded: String, + tab_width: usize, + }, +} + +impl TabExpandedString { + pub(crate) fn new(s: Cow<'static, str>, tab_width: usize) -> Self { + let expanded = s.replace('\t', &" ".repeat(tab_width)); + if s == expanded { + Self::NoTabs(s) + } else { + Self::WithTabs { + original: s, + expanded, + tab_width, + } + } + } + + pub(crate) fn expanded(&self) -> &str { + match &self { + Self::NoTabs(s) => { + debug_assert!(!s.contains('\t')); + s + } + Self::WithTabs { expanded, .. } => expanded, + } + } + + pub(crate) fn set_tab_width(&mut self, new_tab_width: usize) { + if let Self::WithTabs { + original, + expanded, + tab_width, + } = self + { + if *tab_width != new_tab_width { + *tab_width = new_tab_width; + *expanded = original.replace('\t', &" ".repeat(new_tab_width)); + } + } + } +} + +/// Double-smoothed exponentially weighted estimator +/// +/// This uses an exponentially weighted *time-based* estimator, meaning that it exponentially +/// downweights old data based on its age. The rate at which this occurs is currently a constant +/// value of 15 seconds for 90% weighting. This means that all data older than 15 seconds has a +/// collective weight of 0.1 in the estimate, and all data older than 30 seconds has a collective +/// weight of 0.01, and so on. +/// +/// The primary value exposed by `Estimator` is `steps_per_second`. This value is doubly-smoothed, +/// meaning that is the result of using an exponentially weighted estimator (as described above) to +/// estimate the value of another exponentially weighted estimator, which estimates the value of +/// the raw data. +/// +/// The purpose of this extra smoothing step is to reduce instantaneous fluctations in the estimate +/// when large updates are received. Without this, estimates might have a large spike followed by a +/// slow asymptotic approach to zero (until the next spike). +#[derive(Debug)] +pub(crate) struct Estimator { + smoothed_steps_per_sec: f64, + double_smoothed_steps_per_sec: f64, + prev_steps: u64, + prev_time: Instant, + start_time: Instant, +} + +impl Estimator { + fn new(now: Instant) -> Self { + Self { + smoothed_steps_per_sec: 0.0, + double_smoothed_steps_per_sec: 0.0, + prev_steps: 0, + prev_time: now, + start_time: now, + } + } + + fn record(&mut self, new_steps: u64, now: Instant) { + // sanity check: don't record data if time or steps have not advanced + if new_steps <= self.prev_steps || now <= self.prev_time { + // Reset on backwards seek to prevent breakage from seeking to the end for length determination + // See https://github.com/console-rs/indicatif/issues/480 + if new_steps < self.prev_steps { + self.prev_steps = new_steps; + self.reset(now); + } + return; + } + + let delta_steps = new_steps - self.prev_steps; + let delta_t = duration_to_secs(now - self.prev_time); + + // the rate of steps we saw in this update + let new_steps_per_second = delta_steps as f64 / delta_t; + + // update the estimate: a weighted average of the old estimate and new data + let weight = estimator_weight(delta_t); + self.smoothed_steps_per_sec = + self.smoothed_steps_per_sec * weight + new_steps_per_second * (1.0 - weight); + + // An iterative estimate like `smoothed_steps_per_sec` is supposed to be an exponentially + // weighted average from t=0 back to t=-inf; Since we initialize it to 0, we neglect the + // (non-existent) samples in the weighted average prior to the first one, so the resulting + // average must be normalized. We normalize the single estimate here in order to use it as + // a source for the double smoothed estimate. See comment on normalization in + // `steps_per_second` for details. + let delta_t_start = duration_to_secs(now - self.start_time); + let total_weight = 1.0 - estimator_weight(delta_t_start); + let normalized_smoothed_steps_per_sec = self.smoothed_steps_per_sec / total_weight; + + // determine the double smoothed value (EWA smoothing of the single EWA) + self.double_smoothed_steps_per_sec = self.double_smoothed_steps_per_sec * weight + + normalized_smoothed_steps_per_sec * (1.0 - weight); + + self.prev_steps = new_steps; + self.prev_time = now; + } + + /// Reset the state of the estimator. Once reset, estimates will not depend on any data prior + /// to `now`. This does not reset the stored position of the progress bar. + pub(crate) fn reset(&mut self, now: Instant) { + self.smoothed_steps_per_sec = 0.0; + self.double_smoothed_steps_per_sec = 0.0; + + // only reset prev_time, not prev_steps + self.prev_time = now; + self.start_time = now; + } + + /// Average time per step in seconds, using double exponential smoothing + fn steps_per_second(&self, now: Instant) -> f64 { + // Because the value stored in the Estimator is only updated when the Estimator receives an + // update, this value will become stuck if progress stalls. To return an accurate estimate, + // we determine how much time has passed since the last update, and treat this as a + // pseudo-update with 0 steps. + let delta_t = duration_to_secs(now - self.prev_time); + let reweight = estimator_weight(delta_t); + + // Normalization of estimates: + // + // The raw estimate is a single value (smoothed_steps_per_second) that is iteratively + // updated. At each update, the previous value of the estimate is downweighted according to + // its age, receiving the iterative weight W(t) = 0.1 ^ (t/15). + // + // Since W(Sum(t_n)) = Prod(W(t_n)), the total weight of a sample after a series of + // iterative steps is simply W(t_e) - W(t_b), where t_e is the time since the end of the + // sample, and t_b is the time since the beginning. The resulting estimate is therefore a + // weighted average with sample weights W(t_e) - W(t_b). + // + // Notice that the weighting function generates sample weights that sum to 1 only when the + // sample times span from t=0 to t=inf; but this is not the case. We have a first sample + // with finite, positive t_b = t_f. In the raw estimate, we handle times prior to t_f by + // setting an initial value of 0, meaning that these (non-existent) samples have no weight. + // + // Therefore, the raw estimate must be normalized by dividing it by the sum of the weights + // in the weighted average. This sum is just W(0) - W(t_f), where t_f is the time since the + // first sample, and W(0) = 1. + let delta_t_start = duration_to_secs(now - self.start_time); + let total_weight = 1.0 - estimator_weight(delta_t_start); + + // Generate updated values for `smoothed_steps_per_sec` and `double_smoothed_steps_per_sec` + // (sps and dsps) without storing them. Note that we normalize sps when using it as a + // source to update dsps, and then normalize dsps itself before returning it. + let sps = self.smoothed_steps_per_sec * reweight / total_weight; + let dsps = self.double_smoothed_steps_per_sec * reweight + sps * (1.0 - reweight); + dsps / total_weight + } +} + +pub(crate) struct AtomicPosition { + pub(crate) pos: AtomicU64, + capacity: AtomicU8, + prev: AtomicU64, + start: Instant, +} + +impl AtomicPosition { + pub(crate) fn new() -> Self { + Self { + pos: AtomicU64::new(0), + capacity: AtomicU8::new(MAX_BURST), + prev: AtomicU64::new(0), + start: Instant::now(), + } + } + + pub(crate) fn allow(&self, now: Instant) -> bool { + if now < self.start { + return false; + } + + let mut capacity = self.capacity.load(Ordering::Acquire); + // `prev` is the number of ms after `self.started` we last returned `true`, in ns + let prev = self.prev.load(Ordering::Acquire); + // `elapsed` is the number of ns since `self.started` + let elapsed = (now - self.start).as_nanos() as u64; + // `diff` is the number of ns since we last returned `true` + let diff = elapsed.saturating_sub(prev); + + // If `capacity` is 0 and not enough time (1ms) has passed since `prev` + // to add new capacity, return `false`. The goal of this method is to + // make this decision as efficient as possible. + if capacity == 0 && diff < INTERVAL { + return false; + } + + // We now calculate `new`, the number of ms, in ns, since we last returned `true`, + // and `remainder`, which represents a number of ns less than 1ms which we cannot + // convert into capacity now, so we're saving it for later. We do this by + // substracting this from `elapsed` before storing it into `self.prev`. + let (new, remainder) = ((diff / INTERVAL), (diff % INTERVAL)); + // We add `new` to `capacity`, subtract one for returning `true` from here, + // then make sure it does not exceed a maximum of `MAX_BURST`. + capacity = Ord::min(MAX_BURST as u128, (capacity as u128) + (new as u128) - 1) as u8; + + // Then, we just store `capacity` and `prev` atomically for the next iteration + self.capacity.store(capacity, Ordering::Release); + self.prev.store(elapsed - remainder, Ordering::Release); + true + } + + fn reset(&self, now: Instant) { + self.set(0); + let elapsed = (now.saturating_duration_since(self.start)).as_millis() as u64; + self.prev.store(elapsed, Ordering::Release); + } + + pub(crate) fn inc(&self, delta: u64) { + self.pos.fetch_add(delta, Ordering::SeqCst); + } + + pub(crate) fn set(&self, pos: u64) { + self.pos.store(pos, Ordering::Release); + } +} + +const INTERVAL: u64 = 1_000_000; +const MAX_BURST: u8 = 10; + +/// Behavior of a progress bar when it is finished +/// +/// This is invoked when a [`ProgressBar`] or [`ProgressBarIter`] completes and +/// [`ProgressBar::is_finished`] is false. +/// +/// [`ProgressBar`]: crate::ProgressBar +/// [`ProgressBarIter`]: crate::ProgressBarIter +/// [`ProgressBar::is_finished`]: crate::ProgressBar::is_finished +#[derive(Clone, Debug)] +pub enum ProgressFinish { + /// Finishes the progress bar and leaves the current message + /// + /// Same behavior as calling [`ProgressBar::finish()`](crate::ProgressBar::finish). + AndLeave, + /// Finishes the progress bar and sets a message + /// + /// Same behavior as calling [`ProgressBar::finish_with_message()`](crate::ProgressBar::finish_with_message). + WithMessage(Cow<'static, str>), + /// Finishes the progress bar and completely clears it (this is the default) + /// + /// Same behavior as calling [`ProgressBar::finish_and_clear()`](crate::ProgressBar::finish_and_clear). + AndClear, + /// Finishes the progress bar and leaves the current message and progress + /// + /// Same behavior as calling [`ProgressBar::abandon()`](crate::ProgressBar::abandon). + Abandon, + /// Finishes the progress bar and sets a message, and leaves the current progress + /// + /// Same behavior as calling [`ProgressBar::abandon_with_message()`](crate::ProgressBar::abandon_with_message). + AbandonWithMessage(Cow<'static, str>), +} + +impl Default for ProgressFinish { + fn default() -> Self { + Self::AndClear + } +} + +/// Get the appropriate dilution weight for Estimator data given the data's age (in seconds) +/// +/// Whenever an update occurs, we will create a new estimate using a weight `w_i` like so: +/// +/// ```math +/// = * w_i + * (1 - w_i) +/// ``` +/// +/// In other words, the new estimate is a weighted average of the previous estimate and the new +/// data. We want to choose weights such that for any set of samples where `t_0, t_1, ...` are +/// the durations of the samples: +/// +/// ```math +/// Sum(t_i) = ews ==> Prod(w_i) = 0.1 +/// ``` +/// +/// With this constraint it is easy to show that +/// +/// ```math +/// w_i = 0.1 ^ (t_i / ews) +/// ``` +/// +/// Notice that the constraint implies that estimates are independent of the durations of the +/// samples, a very useful feature. +fn estimator_weight(age: f64) -> f64 { + const EXPONENTIAL_WEIGHTING_SECONDS: f64 = 15.0; + 0.1_f64.powf(age / EXPONENTIAL_WEIGHTING_SECONDS) +} + +fn duration_to_secs(d: Duration) -> f64 { + d.as_secs() as f64 + f64::from(d.subsec_nanos()) / 1_000_000_000f64 +} + +fn secs_to_duration(s: f64) -> Duration { + let secs = s.trunc() as u64; + let nanos = (s.fract() * 1_000_000_000f64) as u32; + Duration::new(secs, nanos) +} + +#[derive(Debug)] +pub(crate) enum Status { + InProgress, + DoneVisible, + DoneHidden, +} + +pub(crate) const DEFAULT_TAB_WIDTH: usize = 8; + +#[cfg(test)] +mod tests { + use super::*; + use crate::ProgressBar; + + // https://github.com/rust-lang/rust-clippy/issues/10281 + #[allow(clippy::uninlined_format_args)] + #[test] + fn test_steps_per_second() { + let test_rate = |items_per_second| { + let mut now = Instant::now(); + let mut est = Estimator::new(now); + let mut pos = 0; + + for _ in 0..20 { + pos += items_per_second; + now += Duration::from_secs(1); + est.record(pos, now); + } + let avg_steps_per_second = est.steps_per_second(now); + + assert!(avg_steps_per_second > 0.0); + assert!(avg_steps_per_second.is_finite()); + + let absolute_error = (avg_steps_per_second - items_per_second as f64).abs(); + let relative_error = absolute_error / items_per_second as f64; + assert!( + relative_error < 1.0 / 1e9, + "Expected rate: {}, actual: {}, relative error: {}", + items_per_second, + avg_steps_per_second, + relative_error + ); + }; + + test_rate(1); + test_rate(1_000); + test_rate(1_000_000); + test_rate(1_000_000_000); + test_rate(1_000_000_001); + test_rate(100_000_000_000); + test_rate(1_000_000_000_000); + test_rate(100_000_000_000_000); + test_rate(1_000_000_000_000_000); + } + + #[test] + fn test_double_exponential_ave() { + let mut now = Instant::now(); + let mut est = Estimator::new(now); + let mut pos = 0; + + // note: this is the default weight set in the Estimator + let weight = 15; + + for _ in 0..weight { + pos += 1; + now += Duration::from_secs(1); + est.record(pos, now); + } + now += Duration::from_secs(weight); + + // The first level EWA: + // -> 90% weight @ 0 eps, 9% weight @ 1 eps, 1% weight @ 0 eps + // -> then normalized by deweighting the 1% weight (before -30 seconds) + let single_target = 0.09 / 0.99; + + // The second level EWA: + // -> same logic as above, but using the first level EWA as the source + let double_target = (0.9 * single_target + 0.09) / 0.99; + assert_eq!(est.steps_per_second(now), double_target); + } + + #[test] + fn test_estimator_rewind_position() { + let mut now = Instant::now(); + let mut est = Estimator::new(now); + + now += Duration::from_secs(1); + est.record(1, now); + + // should not panic + now += Duration::from_secs(1); + est.record(0, now); + + // check that reset occurred (estimator at 1 event per sec) + now += Duration::from_secs(1); + est.record(1, now); + assert_eq!(est.steps_per_second(now), 1.0); + + // check that progress bar handles manual seeking + let pb = ProgressBar::hidden(); + pb.set_length(10); + pb.set_position(1); + pb.tick(); + // Should not panic. + pb.set_position(0); + } + + #[test] + fn test_reset_eta() { + let mut now = Instant::now(); + let mut est = Estimator::new(now); + + // two per second, then reset + now += Duration::from_secs(1); + est.record(2, now); + est.reset(now); + + // now one per second, and verify + now += Duration::from_secs(1); + est.record(3, now); + assert_eq!(est.steps_per_second(now), 1.0); + } + + #[test] + fn test_duration_stuff() { + let duration = Duration::new(42, 100_000_000); + let secs = duration_to_secs(duration); + assert_eq!(secs_to_duration(secs), duration); + } + + #[test] + fn test_atomic_position_large_time_difference() { + let atomic_position = AtomicPosition::new(); + let later = atomic_position.start + Duration::from_nanos(INTERVAL * u64::from(u8::MAX)); + // Should not panic. + atomic_position.allow(later); + } +} diff --git a/vendor/indicatif/src/style.rs b/vendor/indicatif/src/style.rs new file mode 100644 index 0000000..01b220f --- /dev/null +++ b/vendor/indicatif/src/style.rs @@ -0,0 +1,987 @@ +use std::collections::HashMap; +use std::fmt::{self, Write}; +use std::mem; +#[cfg(not(target_arch = "wasm32"))] +use std::time::Instant; + +use console::{measure_text_width, Style}; +#[cfg(target_arch = "wasm32")] +use instant::Instant; +#[cfg(feature = "unicode-segmentation")] +use unicode_segmentation::UnicodeSegmentation; + +use crate::format::{ + BinaryBytes, DecimalBytes, FormattedDuration, HumanBytes, HumanCount, HumanDuration, + HumanFloatCount, +}; +use crate::state::{ProgressState, TabExpandedString, DEFAULT_TAB_WIDTH}; + +#[derive(Clone)] +pub struct ProgressStyle { + tick_strings: Vec>, + progress_chars: Vec>, + template: Template, + // how unicode-big each char in progress_chars is + char_width: usize, + tab_width: usize, + pub(crate) format_map: HashMap<&'static str, Box>, +} + +#[cfg(feature = "unicode-segmentation")] +fn segment(s: &str) -> Vec> { + UnicodeSegmentation::graphemes(s, true) + .map(|s| s.into()) + .collect() +} + +#[cfg(not(feature = "unicode-segmentation"))] +fn segment(s: &str) -> Vec> { + s.chars().map(|x| x.to_string().into()).collect() +} + +#[cfg(feature = "unicode-width")] +fn measure(s: &str) -> usize { + unicode_width::UnicodeWidthStr::width(s) +} + +#[cfg(not(feature = "unicode-width"))] +fn measure(s: &str) -> usize { + s.chars().count() +} + +/// finds the unicode-aware width of the passed grapheme cluters +/// panics on an empty parameter, or if the characters are not equal-width +fn width(c: &[Box]) -> usize { + c.iter() + .map(|s| measure(s.as_ref())) + .fold(None, |acc, new| { + match acc { + None => return Some(new), + Some(old) => assert_eq!(old, new, "got passed un-equal width progress characters"), + } + acc + }) + .unwrap() +} + +impl ProgressStyle { + /// Returns the default progress bar style for bars + pub fn default_bar() -> Self { + Self::new(Template::from_str("{wide_bar} {pos}/{len}").unwrap()) + } + + /// Returns the default progress bar style for spinners + pub fn default_spinner() -> Self { + Self::new(Template::from_str("{spinner} {msg}").unwrap()) + } + + /// Sets the template string for the progress bar + /// + /// Review the [list of template keys](../index.html#templates) for more information. + pub fn with_template(template: &str) -> Result { + Ok(Self::new(Template::from_str(template)?)) + } + + pub(crate) fn set_tab_width(&mut self, new_tab_width: usize) { + self.tab_width = new_tab_width; + self.template.set_tab_width(new_tab_width); + } + + fn new(template: Template) -> Self { + let progress_chars = segment("█░"); + let char_width = width(&progress_chars); + Self { + tick_strings: "⠁⠁⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈⠈ " + .chars() + .map(|c| c.to_string().into()) + .collect(), + progress_chars, + char_width, + template, + format_map: HashMap::default(), + tab_width: DEFAULT_TAB_WIDTH, + } + } + + /// Sets the tick character sequence for spinners + /// + /// Note that the last character is used as the [final tick string][Self::get_final_tick_str()]. + /// At least two characters are required to provide a non-final and final state. + pub fn tick_chars(mut self, s: &str) -> Self { + self.tick_strings = s.chars().map(|c| c.to_string().into()).collect(); + // Format bar will panic with some potentially confusing message, better to panic here + // with a message explicitly informing of the problem + assert!( + self.tick_strings.len() >= 2, + "at least 2 tick chars required" + ); + self + } + + /// Sets the tick string sequence for spinners + /// + /// Note that the last string is used as the [final tick string][Self::get_final_tick_str()]. + /// At least two strings are required to provide a non-final and final state. + pub fn tick_strings(mut self, s: &[&str]) -> Self { + self.tick_strings = s.iter().map(|s| s.to_string().into()).collect(); + // Format bar will panic with some potentially confusing message, better to panic here + // with a message explicitly informing of the problem + assert!( + self.progress_chars.len() >= 2, + "at least 2 tick strings required" + ); + self + } + + /// Sets the progress characters `(filled, current, to do)` + /// + /// You can pass more than three for a more detailed display. + /// All passed grapheme clusters need to be of equal width. + pub fn progress_chars(mut self, s: &str) -> Self { + self.progress_chars = segment(s); + // Format bar will panic with some potentially confusing message, better to panic here + // with a message explicitly informing of the problem + assert!( + self.progress_chars.len() >= 2, + "at least 2 progress chars required" + ); + self.char_width = width(&self.progress_chars); + self + } + + /// Adds a custom key that owns a [`ProgressTracker`] to the template + pub fn with_key(mut self, key: &'static str, f: S) -> Self { + self.format_map.insert(key, Box::new(f)); + self + } + + /// Sets the template string for the progress bar + /// + /// Review the [list of template keys](../index.html#templates) for more information. + pub fn template(mut self, s: &str) -> Result { + self.template = Template::from_str(s)?; + Ok(self) + } + + fn current_tick_str(&self, state: &ProgressState) -> &str { + match state.is_finished() { + true => self.get_final_tick_str(), + false => self.get_tick_str(state.tick), + } + } + + /// Returns the tick string for a given number + pub fn get_tick_str(&self, idx: u64) -> &str { + &self.tick_strings[(idx as usize) % (self.tick_strings.len() - 1)] + } + + /// Returns the tick string for the finished state + pub fn get_final_tick_str(&self) -> &str { + &self.tick_strings[self.tick_strings.len() - 1] + } + + fn format_bar(&self, fract: f32, width: usize, alt_style: Option<&Style>) -> BarDisplay<'_> { + // The number of clusters from progress_chars to write (rounding down). + let width = width / self.char_width; + // The number of full clusters (including a fractional component for a partially-full one). + let fill = fract * width as f32; + // The number of entirely full clusters (by truncating `fill`). + let entirely_filled = fill as usize; + // 1 if the bar is not entirely empty or full (meaning we need to draw the "current" + // character between the filled and "to do" segment), 0 otherwise. + let head = usize::from(fill > 0.0 && entirely_filled < width); + + let cur = if head == 1 { + // Number of fine-grained progress entries in progress_chars. + let n = self.progress_chars.len().saturating_sub(2); + let cur_char = if n <= 1 { + // No fine-grained entries. 1 is the single "current" entry if we have one, the "to + // do" entry if not. + 1 + } else { + // Pick a fine-grained entry, ranging from the last one (n) if the fractional part + // of fill is 0 to the first one (1) if the fractional part of fill is almost 1. + n.saturating_sub((fill.fract() * n as f32) as usize) + }; + Some(cur_char) + } else { + None + }; + + // Number of entirely empty clusters needed to fill the bar up to `width`. + let bg = width.saturating_sub(entirely_filled).saturating_sub(head); + let rest = RepeatedStringDisplay { + str: &self.progress_chars[self.progress_chars.len() - 1], + num: bg, + }; + + BarDisplay { + chars: &self.progress_chars, + filled: entirely_filled, + cur, + rest: alt_style.unwrap_or(&Style::new()).apply_to(rest), + } + } + + pub(crate) fn format_state( + &self, + state: &ProgressState, + lines: &mut Vec, + target_width: u16, + ) { + let mut cur = String::new(); + let mut buf = String::new(); + let mut wide = None; + + let pos = state.pos(); + let len = state.len().unwrap_or(pos); + for part in &self.template.parts { + match part { + TemplatePart::Placeholder { + key, + align, + width, + truncate, + style, + alt_style, + } => { + buf.clear(); + if let Some(tracker) = self.format_map.get(key.as_str()) { + tracker.write(state, &mut TabRewriter(&mut buf, self.tab_width)); + } else { + match key.as_str() { + "wide_bar" => { + wide = Some(WideElement::Bar { alt_style }); + buf.push('\x00'); + } + "bar" => buf + .write_fmt(format_args!( + "{}", + self.format_bar( + state.fraction(), + width.unwrap_or(20) as usize, + alt_style.as_ref(), + ) + )) + .unwrap(), + "spinner" => buf.push_str(self.current_tick_str(state)), + "wide_msg" => { + wide = Some(WideElement::Message { align }); + buf.push('\x00'); + } + "msg" => buf.push_str(state.message.expanded()), + "prefix" => buf.push_str(state.prefix.expanded()), + "pos" => buf.write_fmt(format_args!("{pos}")).unwrap(), + "human_pos" => { + buf.write_fmt(format_args!("{}", HumanCount(pos))).unwrap(); + } + "len" => buf.write_fmt(format_args!("{len}")).unwrap(), + "human_len" => { + buf.write_fmt(format_args!("{}", HumanCount(len))).unwrap(); + } + "percent" => buf + .write_fmt(format_args!("{:.*}", 0, state.fraction() * 100f32)) + .unwrap(), + "bytes" => buf.write_fmt(format_args!("{}", HumanBytes(pos))).unwrap(), + "total_bytes" => { + buf.write_fmt(format_args!("{}", HumanBytes(len))).unwrap(); + } + "decimal_bytes" => buf + .write_fmt(format_args!("{}", DecimalBytes(pos))) + .unwrap(), + "decimal_total_bytes" => buf + .write_fmt(format_args!("{}", DecimalBytes(len))) + .unwrap(), + "binary_bytes" => { + buf.write_fmt(format_args!("{}", BinaryBytes(pos))).unwrap(); + } + "binary_total_bytes" => { + buf.write_fmt(format_args!("{}", BinaryBytes(len))).unwrap(); + } + "elapsed_precise" => buf + .write_fmt(format_args!("{}", FormattedDuration(state.elapsed()))) + .unwrap(), + "elapsed" => buf + .write_fmt(format_args!("{:#}", HumanDuration(state.elapsed()))) + .unwrap(), + "per_sec" => buf + .write_fmt(format_args!("{}/s", HumanFloatCount(state.per_sec()))) + .unwrap(), + "bytes_per_sec" => buf + .write_fmt(format_args!("{}/s", HumanBytes(state.per_sec() as u64))) + .unwrap(), + "binary_bytes_per_sec" => buf + .write_fmt(format_args!( + "{}/s", + BinaryBytes(state.per_sec() as u64) + )) + .unwrap(), + "eta_precise" => buf + .write_fmt(format_args!("{}", FormattedDuration(state.eta()))) + .unwrap(), + "eta" => buf + .write_fmt(format_args!("{:#}", HumanDuration(state.eta()))) + .unwrap(), + "duration_precise" => buf + .write_fmt(format_args!("{}", FormattedDuration(state.duration()))) + .unwrap(), + "duration" => buf + .write_fmt(format_args!("{:#}", HumanDuration(state.duration()))) + .unwrap(), + _ => (), + } + }; + + match width { + Some(width) => { + let padded = PaddedStringDisplay { + str: &buf, + width: *width as usize, + align: *align, + truncate: *truncate, + }; + match style { + Some(s) => cur + .write_fmt(format_args!("{}", s.apply_to(padded))) + .unwrap(), + None => cur.write_fmt(format_args!("{padded}")).unwrap(), + } + } + None => match style { + Some(s) => cur.write_fmt(format_args!("{}", s.apply_to(&buf))).unwrap(), + None => cur.push_str(&buf), + }, + } + } + TemplatePart::Literal(s) => cur.push_str(s.expanded()), + TemplatePart::NewLine => { + self.push_line(lines, &mut cur, state, &mut buf, target_width, &wide); + } + } + } + + if !cur.is_empty() { + self.push_line(lines, &mut cur, state, &mut buf, target_width, &wide); + } + } + + fn push_line( + &self, + lines: &mut Vec, + cur: &mut String, + state: &ProgressState, + buf: &mut String, + target_width: u16, + wide: &Option, + ) { + let expanded = match wide { + Some(inner) => inner.expand(mem::take(cur), self, state, buf, target_width), + None => mem::take(cur), + }; + + // If there are newlines, we need to split them up + // and add the lines separately so that they're counted + // correctly on re-render. + for (i, line) in expanded.split('\n').enumerate() { + // No newlines found in this case + if i == 0 && line.len() == expanded.len() { + lines.push(expanded); + break; + } + + lines.push(line.to_string()); + } + } +} + +struct TabRewriter<'a>(&'a mut dyn fmt::Write, usize); + +impl Write for TabRewriter<'_> { + fn write_str(&mut self, s: &str) -> fmt::Result { + self.0 + .write_str(s.replace('\t', &" ".repeat(self.1)).as_str()) + } +} + +#[derive(Clone, Copy)] +enum WideElement<'a> { + Bar { alt_style: &'a Option