prodash-28.0.0/.cargo_vcs_info.json0000644000000001360000000000100125630ustar { "git": { "sha1": "986909cc4eef785c4f5e9f5f8184723a21ccb721" }, "path_in_vcs": "" }prodash-28.0.0/CHANGELOG.md000064400000000000000000004154701046102023000131770ustar 00000000000000# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## 28.0.0 (2023-12-29) ### Chore (BREAKING) - upgrade `ratatui` and `crosstermion` to latest versions. ### Commit Statistics - 1 commit contributed to the release. - 21 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Upgrade `ratatui` and `crosstermion` to latest versions. ([`18686db`](https://github.com/byron/prodash/commit/18686dbd32e6920ab5d7271c32481f7f41eae4de))
## 27.0.0 (2023-12-07) ### New Features - Change duration formatting to be more human readable. Note that this changes duration output from something like `69d10h40m` to `69d 10h 40m`. ### Reverted (BREAKING) - All `termion`-related features are now removed and obsolete. After the most recent update, certion event-related features in crosstermion stopped working in the context of the GUI, so it's probably best to let it go. By now, `crosstermion` is also very much a more portable replacement. ### Commit Statistics - 8 commits contributed to the release over the course of 44 calendar days. - 89 days passed between releases. - 2 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Thanks Clippy [Clippy](https://github.com/rust-lang/rust-clippy) helped 1 time to make code idiomatic. ### Commit Details
view details * **Uncategorized** - Release prodash v27.0.0 ([`967e99a`](https://github.com/byron/prodash/commit/967e99a034cf1f570864b068d9c00baa54290b19)) - Merge branch 'replace-ansi_term' ([`3a50a18`](https://github.com/byron/prodash/commit/3a50a18551c98cb3e0fa8362062e0f6fd41d5d34)) - All `termion`-related features are now removed and obsolete. ([`b1fd37d`](https://github.com/byron/prodash/commit/b1fd37d272c59249c151e1dbf498d5e2767f5507)) - Thanks clippy ([`3c28eb0`](https://github.com/byron/prodash/commit/3c28eb04e42cf0b2d47e4a6538c2436e8abac274)) - Upgrade to `crossterm` v0.27. ([`34397f1`](https://github.com/byron/prodash/commit/34397f1b39dca0d585326728681a70b654db7118)) - Change duration formatting to be more human readable. ([`6acf6fe`](https://github.com/byron/prodash/commit/6acf6fe22a9ec4f7a16626db556c051c2084ccb2)) - Refactor ([`d032106`](https://github.com/byron/prodash/commit/d0321067244ed3d76eedc0078c881af2ea603c5b)) - Replace compound_duration with humantime This is the humantime part of PR https://github.com/Byron/prodash/pull/25 ([`f5143c9`](https://github.com/byron/prodash/commit/f5143c98be0f2f2267c4574805f81a61cd8d613a))
## 26.2.2 (2023-09-09) This release relaxes trait-bounds of `Count`, `Progress` and `NestedProgress` to allow `?Sized` as well. ### Commit Statistics - 5 commits contributed to the release. - 2 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Release prodash v26.2.2 ([`bb1eadf`](https://github.com/byron/prodash/commit/bb1eadfc9042cc27b346cc064115a69163f10c05)) - Prepare next release ([`414506d`](https://github.com/byron/prodash/commit/414506df859584d17eb8ec755534040b813fa953)) - Merge pull request #24 from NobodyXu/fix/dyn ([`c905d5c`](https://github.com/byron/prodash/commit/c905d5cb8d62368bb4891cc732ce613d09d6498b)) - Relax bound of `Progress`, `Count` impl for `DynNestedProgressToNestedProgress` ([`967ea46`](https://github.com/byron/prodash/commit/967ea46b71c2bb44f7cd75524d101c4bfd2df0bf)) - Fix use of `DynNestedProgress` as trait object ([`7eb69ac`](https://github.com/byron/prodash/commit/7eb69ac3f4946c72a0598b3021153045f3e9626b))
## 26.2.1 (2023-09-06) ### Bug Fixes - Add missing forwardings for various methods. Not having these could lead to incorrect thoughput display. ### Commit Statistics - 2 commits contributed to the release. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Release prodash v26.2.1 ([`f37161f`](https://github.com/byron/prodash/commit/f37161f49f65991fd97ad855cf23a4f0ace3c9bb)) - Add missing forwardings for various methods. ([`53ea2b8`](https://github.com/byron/prodash/commit/53ea2b81292a5062816bb12af0ab4f7931d5d2fa))
## 26.2.0 (2023-09-05) ### New Features - add `BoxedProgress` type that implements `Progress`. This makes working with boxed progress even more flexible. ### Commit Statistics - 2 commits contributed to the release. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Release prodash v26.2.0 ([`8f26902`](https://github.com/byron/prodash/commit/8f2690254576e32624abbfc4b5e18283e7e542f7)) - Add `BoxedProgress` type that implements `Progress`. ([`b3cae19`](https://github.com/byron/prodash/commit/b3cae191413653feef62808b60b7eea52145e55e))
## 26.1.0 (2023-09-04) ### New Features - add `progress::AtomicStep` to allow referring to it. Previously, only `StepShared` was available, which implies an `Arc`. ### Commit Statistics - 2 commits contributed to the release. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Release prodash v26.1.0 ([`05bc923`](https://github.com/byron/prodash/commit/05bc92395aeeaf498ab46e479decf1ae71382835)) - Add `progress::AtomicStep` to allow referring to it. ([`0705a73`](https://github.com/byron/prodash/commit/0705a734b401d44657be59e83e4cbf58cb4484a8))
## 26.0.0 (2023-09-04) This release is all about making `dyn` possible both for nested progress, as well as for 'simple' one (previously known as `RawProgress`). Switching to this release naturally makes it possible for users of `Progress` to also use `dyn Progress`, as this trait is now object safe (formerly `RawProgress`). If there are compile errors, the code now needs `NestedProgress`, instead of `Progress`, and possibly the import of the `Count` trait. Finally, it's recommended to review all usages of `Progress` as they can possibly be replaced with `Count` which provides the guarantee that only counting happens, and no change of the progress information itself. ### New Features (BREAKING) - split `Progress` into various super-traits to allow most of them to be dyn-safe. `Progress` is now `NestedProgress`, `RawProgress` is now `Progress`, and there is a new `Count` trait for solely counting things. - `Progress::counter()` is now mandatory. This should simplify downstream code and we just accept that we are dealing with a threaded world. This also comes with performance improvements as increments are now 250% faster. ### Commit Statistics - 9 commits contributed to the release. - 13 days passed between releases. - 2 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Release prodash v26.0.0 ([`6b94f28`](https://github.com/byron/prodash/commit/6b94f28102b3133e4c1cba3765b55108c528b534)) - Merge branch 'simplify' ([`5e21df7`](https://github.com/byron/prodash/commit/5e21df796d4c8ddefd71ee1a35df3b591c6d5e58)) - `Progress::counter()` is now mandatory. ([`6c60835`](https://github.com/byron/prodash/commit/6c60835ae49487a220b1817bc6cea3ebc8bf1aff)) - Prepare release ([`e1e282a`](https://github.com/byron/prodash/commit/e1e282aa37fc753884407339196809f2b0b72d2d)) - Fixup nested dyn-traits ([`5e76abf`](https://github.com/byron/prodash/commit/5e76abfbf8dc18afddea68873c50cce677450a54)) - Merge branch 'feat/dyn-progress' into simplify ([`c1590e4`](https://github.com/byron/prodash/commit/c1590e4650a9ffcf96a216f6a9fe82a1cf7cc10e)) - Split `Progress` into various super-traits to allow most of them to be dyn-safe. ([`6aba6e3`](https://github.com/byron/prodash/commit/6aba6e34f3e39bba5e609a0bc780c758cb43c821)) - Add benchmarks for dyn-traits ([`9d03124`](https://github.com/byron/prodash/commit/9d03124667935314a9d4c8e52886b994967f2671)) - Refactor ([`54094b6`](https://github.com/byron/prodash/commit/54094b63289446f0582c6b49a666b5d993625dff))
## 25.0.2 (2023-08-22) ### Chore - Adjusting changelogs prior to release of prodash v25.0.2 ### New Features - Add new trait `DynProgress` & type `BoxedDynProgress` ### Commit Statistics - 4 commits contributed to the release over the course of 2 calendar days. - 37 days passed between releases. - 2 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Release prodash v25.0.2 ([`abcc61b`](https://github.com/byron/prodash/commit/abcc61b456e75d4f700f3c476849d7c36c3ece15)) - Adjusting changelogs prior to release of prodash v25.0.2 ([`0574176`](https://github.com/byron/prodash/commit/05741765491984487beea7326eff9863b669ab51)) - Remove `atty` in favor of `is-terminal` ([`2bfe9ad`](https://github.com/byron/prodash/commit/2bfe9adca357cf48d7310036684eeb85efa5ef46)) - Add new trait `DynProgress` & type `BoxedDynProgress` ([`24d0b2a`](https://github.com/byron/prodash/commit/24d0b2aaa58978990fea90c2f3b387e238acf966))
## 25.0.1 (2023-07-16) ### Bug Fixes - `log` progress now supports a shared counter, just like the tree-item implementation ### Commit Statistics - 4 commits contributed to the release. - 60 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Release prodash v25.0.1 ([`3ad8226`](https://github.com/byron/prodash/commit/3ad8226fd7825669d843a9c1bde001de1b702049)) - Merge branch 'log-fixes' ([`a38d22c`](https://github.com/byron/prodash/commit/a38d22cf10068ce4275d65eb3e74652c2977b7e2)) - Upgrade criterion to the latest version, it compiles more quickly. ([`78272c0`](https://github.com/byron/prodash/commit/78272c0f2a243b9e07165813a0eacc6ade623b90)) - `log` progress now supports a shared counter, just like the tree-item implementation ([`17dd8f6`](https://github.com/byron/prodash/commit/17dd8f64563dd96ca454494e24a7f229716f6b1f))
## 25.0.0 (2023-05-16) ### New Features - Introduce the object-safe `RawProgress` trait. It's automatically implemented for `Progress` and allows for more flexible use of progress particularly in leaf nodes. This is useful if a function needs to take multiple types of progress as it is called from different places in the same function. Without dyn-traits, it's not possible to make such call. ### New Features (BREAKING) - Make messaging functions thread-safe by taking shared borrow and requring `Sync`. That way it's possible to share the `RawProgress` object across threads and emit messages, much like a logging system that's more integrated with rendering. ### Commit Statistics - 3 commits contributed to the release. - 5 days passed between releases. - 2 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Release prodash v25.0.0 ([`02fcb9b`](https://github.com/byron/prodash/commit/02fcb9bd40ff9c03a46ce68926995a9614328ae0)) - Make messaging functions thread-safe by taking shared borrow and requring `Sync`. ([`84d96c7`](https://github.com/byron/prodash/commit/84d96c7b6ab07462d6c20147958d5aa1a58a688e)) - Introduce the object-safe `RawProgress` trait. ([`8941f4b`](https://github.com/byron/prodash/commit/8941f4b5b9c0d00dfd7b82c756b128982f163a06))
## 24.0.0 (2023-05-11) ### Chore (BREAKING) - switch from `tui` to `ratatui`. The latter is a maintained fork. ### Commit Statistics - 4 commits contributed to the release. - 60 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Thanks Clippy [Clippy](https://github.com/rust-lang/rust-clippy) helped 1 time to make code idiomatic. ### Commit Details
view details * **Uncategorized** - Release prodash v24.0.0 ([`41ad0a4`](https://github.com/byron/prodash/commit/41ad0a45ea39e6b283f808768ea60495b3d2b65f)) - Merge branch 'ratatui' ([`4920457`](https://github.com/byron/prodash/commit/492045793a23be9ebda7310d89593288a2bd3340)) - Thanks clippy ([`25356a3`](https://github.com/byron/prodash/commit/25356a369c345b1e89f7c6c356ff7142020849de)) - Switch from `tui` to `ratatui`. ([`fe5d017`](https://github.com/byron/prodash/commit/fe5d01736179271f6b7bf20367f5d0e2bb616c4a))
## 23.1.2 (2023-03-11) ### Bug Fixes - line renderer now properly detects changes. Previously change-detection was implemented based on the assumption that the progress tree is copied entirely. Now, however, the interesting values are shared. The change-detection was adjusted to keep the state's hash of the most recent drawing, instead of doing everything in line, which saves time hashing as well. ### Commit Statistics - 2 commits contributed to the release. - 9 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Release prodash v23.1.2 ([`c15f8db`](https://github.com/byron/prodash/commit/c15f8db0a7221ad6326544685200b68cb9ff5ddd)) - Line renderer now properly detects changes. ([`7966f79`](https://github.com/byron/prodash/commit/7966f79cc7009acb33761cee70398b05b0006cc1))
## 23.1.1 (2023-03-02) A maintenance release without user-facing changes. Most notably, `parking_lot` was upgraded to the latest version. ### Commit Statistics - 3 commits contributed to the release. - 2 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Release prodash v23.1.1 ([`a91c52f`](https://github.com/byron/prodash/commit/a91c52f0407a8adf8f93ad66796e1979e08ce126)) - Prepare changelog ([`1a4eb9b`](https://github.com/byron/prodash/commit/1a4eb9b5dea3e100b188be956ab7670f0b8d5ad6)) - Upgrade dependencies, particularly `parking_lot` ([`7ae8a07`](https://github.com/byron/prodash/commit/7ae8a0793752b713c6605be45688ca81fbb7e75e))
## 23.1.0 (2023-02-28) ### New Features - improve performance of `progress::tree` operations by more than 50%. This was done by implementing shared state in a simple Mutex protected hashmap which for typical programs with less contention is faster than using the `dashmap` crate. However, for those who know they need it, the previous implementation is still available in with the `progress-tree-hp-hashmap` feature toggle. ### Commit Statistics - 4 commits contributed to the release. - 61 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Release prodash v23.1.0 ([`45b4b7e`](https://github.com/byron/prodash/commit/45b4b7ea06b08cb30bd1f1ca2c05874bb0cf19ce)) - No need for `unsound-local-offset` anymore. ([`c53ab9d`](https://github.com/byron/prodash/commit/c53ab9d5d238be4aaabfb8a990c80ca1a7c58dec)) - Make fmt ([`490336f`](https://github.com/byron/prodash/commit/490336f9920ccde0ebe8d142dc51b390e9afc899)) - Improve performance of `progress::tree` operations by more than 50%. ([`6f966b4`](https://github.com/byron/prodash/commit/6f966b4f859f1b02775dcb3461bacf46b46ab707))
## 23.0.0 (2022-12-29) ### New Features (BREAKING) - Implement `Hash` for `Task` to avoid redrawing if nothing changes with the Line renderer. That way, if everything stops due to a user prompt, the user's input won't be clobbered continnuously. ### Commit Statistics - 2 commits contributed to the release. - 23 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Release prodash v23.0.0 ([`d527e4b`](https://github.com/byron/prodash/commit/d527e4be160ac80714deeaca11b8ed677f8d842f)) - Implement `Hash` for `Task` to avoid redrawing if nothing changes with the Line renderer. ([`a1db1b2`](https://github.com/byron/prodash/commit/a1db1b27fc7f14be052bbfc660c9c9b174c1d3cc))
## 22.1.0 (2022-12-06) ### New Features - `progress::Key` now supports 6 levels of hierarchy instead of 4. That way it's less likely that surprises occour of more than necessary levels are added. ### Commit Statistics - 2 commits contributed to the release. - 1 day passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Release prodash v22.1.0 ([`2ae17c5`](https://github.com/byron/prodash/commit/2ae17c5bd7ab91b9e05a16fb4f771ce0ce0001f4)) - `progress::Key` now supports 6 levels of hierarchy instead of 4. ([`ea9aa58`](https://github.com/byron/prodash/commit/ea9aa5815ef2d1c734b85c185ddb65ac731b1195))
## 22.0.0 (2022-12-05) ### Chore - switch to Rust edition 2021 ### Changed (BREAKING) - remove `Tree` and `TreeOptions` in favor of `tree::Root` and `tree::root::Options`. Previously it was confusing what a tree Root actually is due to the rename, and ambiguity isn't what we would want here. ### New Features (BREAKING) - `From for tree::Root`, `tree::root::Options::create()` returns `tree::Root` instead of `Arc`. That way we won't be forced to produce an `Arc` if it's not needed. ### Commit Statistics - 5 commits contributed to the release. - 11 days passed between releases. - 3 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Release prodash v22.0.0 ([`8b78fe9`](https://github.com/byron/prodash/commit/8b78fe9614a584096f4fefed38a8965266caf2e9)) - Switch to Rust edition 2021 ([`46aeffd`](https://github.com/byron/prodash/commit/46aeffd13cda49146c8a33e93c8c9b0fbcb15c8b)) - Make fmt ([`6e90cb9`](https://github.com/byron/prodash/commit/6e90cb965377762920d4dbb5debe8d47a4be89a2)) - `From for tree::Root`, `tree::root::Options::create()` returns `tree::Root` instead of `Arc`. ([`edab373`](https://github.com/byron/prodash/commit/edab37364276864d45241c2946173388a0602f23)) - Remove `Tree` and `TreeOptions` in favor of `tree::Root` and `tree::root::Options`. ([`53cb09d`](https://github.com/byron/prodash/commit/53cb09dc0314b0e8ce58dc50e0c07a053b963ccd))
## 21.1.0 (2022-11-23) ### New Features - identify each progress item with `Id` using `add_child_with_id()`. An `Id` is four bytes like b"TREE" that are stable and identify progress items (as created by `add_child(…)` within a function call. Callers may use this knowledge to pick specific progress items for consumption, instead of trying to rely on identifying tasks by name which may change. The identifier can also be queried with `Progress::id()`, even though it could be `prodash::progress::UNKNOWN` if the progress item wasn't created with `add_child_with_id()`. ### Commit Statistics - 4 commits contributed to the release. - 37 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Release prodash v21.1.0 ([`6327e15`](https://github.com/byron/prodash/commit/6327e1587d6886f4279ec037ad587dab52a7cedf)) - `make fmt` ([`5415acc`](https://github.com/byron/prodash/commit/5415acc4cae7bab81a22758fe969bdb8b3deac13)) - Make it easy to format 'use'es in the codebase. ([`5b8e54d`](https://github.com/byron/prodash/commit/5b8e54d959f895d762a4a77d194f3947a6302783)) - Identify each progress item with `Id` using `add_child_with_id()`. ([`c332a6f`](https://github.com/byron/prodash/commit/c332a6f266a6ae0cacf19cb523e551bb63c1e7ea))
## 21.0.0 (2022-10-17) ### New Features - `impl Progress for &mut T: where T: Progress`. This makes it possible to hand borrowed progress implementations to functions that need progress reporting, making the usage of progress easier. ### New Features (BREAKING) - remove `Progress: 'static` requirement. This requirement can be added where used and where needed, and originally snuck in because it was easier and `Progress` implementations typically are `'static` as well. However, that requirement made it impossible to implement `Progoress` for `&mut T where T: Progress`. ### Commit Statistics - 3 commits contributed to the release. - 27 days passed between releases. - 2 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Release prodash v21.0.0 ([`d7fcf5b`](https://github.com/byron/prodash/commit/d7fcf5bb5a6bad8225c22ce6df848835f5790847)) - `impl Progress for &mut T: where T: Progress`. ([`3347a02`](https://github.com/byron/prodash/commit/3347a0294c5b95270c34de9f396214353baea36d)) - Remove `Progress: 'static` requirement. ([`300181b`](https://github.com/byron/prodash/commit/300181bdd4b2ef1822dddd1fe814d7e3e5b26779))
## 20.2.0 (2022-09-20) ### New Features - Add `Progress::set_max()` to set the highgest expected progress value. ### Bug Fixes - don't reset the shared counter value on `init`. It's possible to re-initialize the progress, and when that's done it would detach the counter from previous instances that might have been observed by callers to `counter()`, which is surprising. ### Commit Statistics - 4 commits contributed to the release. - 2 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Release prodash v20.2.0 ([`f49f470`](https://github.com/byron/prodash/commit/f49f4703f5d09f9bbe8ec32bd249c42bd129d7ea)) - Don't reset the shared counter value on `init`. ([`414bc71`](https://github.com/byron/prodash/commit/414bc71e79475335345dbc529566a6efc3afee23)) - Add `Progress::set_max()` to set the highgest expected progress value. ([`4904065`](https://github.com/byron/prodash/commit/4904065a51594b5bc4f32a247e513b45093373fa)) - Make more explicit what is cloned (without altering actual behaviour) ([`b023efd`](https://github.com/byron/prodash/commit/b023efdc9c076f15cef1978ad95d932f782bdea7))
## 20.1.1 (2022-09-20) ### Bug Fixes - implement `Progress::counter()` for all utility types. This was forgotten previously as there was a default implementation right from the start. ### Commit Statistics - 2 commits contributed to the release. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Release prodash v20.1.1 ([`d152701`](https://github.com/byron/prodash/commit/d1527018e5156921c3263c9d9afafad8c488a3b9)) - Implement `Progress::counter()` for all utility types. ([`a0e7da7`](https://github.com/byron/prodash/commit/a0e7da7d08331eeeea8ca0cb6e349fe32ce876bc))
## 20.1.0 (2022-09-20) ### New Features - `Progress::counter()` returns a shared step counter. This is useful if multiple threads want to access the same progress, without the need for provide each their own progress and aggregating the result. ### Commit Statistics - 4 commits contributed to the release. - 5 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Thanks Clippy [Clippy](https://github.com/rust-lang/rust-clippy) helped 1 time to make code idiomatic. ### Commit Details
view details * **Uncategorized** - Release prodash v20.1.0 ([`6591872`](https://github.com/byron/prodash/commit/65918722da151b756f6a3facc3577bd2002feadc)) - Fix compile warnings for some configurations ([`fe0ac06`](https://github.com/byron/prodash/commit/fe0ac0659c5faba9b9c0b699850ffc9e66b566f0)) - Thanks clippy ([`deb7791`](https://github.com/byron/prodash/commit/deb77916c1ced591410aba2593284c6a1345d426)) - `Progress::counter()` returns a shared step counter. ([`08be317`](https://github.com/byron/prodash/commit/08be317dbd77f0fdb9673b9c24c239ad0d5078c3))
## 20.0.1 (2022-09-15) ### Bug Fixes - Allow builds to succeed on Windows by not registering SIGWINCH signal. Without said signal, the render line will not automatically resize anymore, which in theory can be compensated for by re-obtaining the terminal dimensions every now and then (currently not done). ### Commit Statistics - 5 commits contributed to the release. - 2 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 1 unique issue was worked on: [#12](https://github.com/byron/prodash/issues/12) ### Commit Details
view details * **[#12](https://github.com/byron/prodash/issues/12)** - Allow builds to succeed on Windows by not registering SIGWINCH signal. ([`80a4850`](https://github.com/byron/prodash/commit/80a4850802023e5a0f6fe85e6af416aaeb729e21)) - Only register SIGWINCH signal on unix ([`5de765b`](https://github.com/byron/prodash/commit/5de765b6c134be6c439583dd89297fe2b3f1e41f)) - Adjust CI configuration to catch more cross-platform errors ([`b7e0f2c`](https://github.com/byron/prodash/commit/b7e0f2c97bc6c384dfbb5ed390909a5a6850bb74)) * **Uncategorized** - Release prodash v20.0.1 ([`b98595b`](https://github.com/byron/prodash/commit/b98595b8cc9fd2f0bf3ea8cc22fbd7cf6cb07ab1)) - Do away with unsafe code by using safe wrappers instead. ([`447aa0f`](https://github.com/byron/prodash/commit/447aa0f518aaa97dd061813a501e6a0b8512dd88))
## 20.0.0 (2022-09-12) ### New Features - line renderer adjusts when resizing the terminal. ### Chore (BREAKING) - upgrade dependencies to tui `0.19` and crossterm `0.25` ### Commit Statistics - 3 commits contributed to the release. - 175 days passed between releases. - 2 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Release prodash v20.0.0 ([`54e0a6a`](https://github.com/byron/prodash/commit/54e0a6ae39ff53b5c6e7abe9f44d003b833bfed6)) - Line renderer adjusts when resizing the terminal. ([`bab2ea0`](https://github.com/byron/prodash/commit/bab2ea09089e2eb8e4e826bb17211a444aa35f31)) - Upgrade dependencies to tui `0.19` and crossterm `0.25` ([`a3b2678`](https://github.com/byron/prodash/commit/a3b26782dc074c469b5fc480595d2ac9ef8bc9d0))
## 19.0.1 (2022-03-20) ### Bug Fixes - line renderer will clear previous lines if progress is lost Previously it would just exit its main loop and leave lines on screen. ### Commit Statistics - 3 commits contributed to the release. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Release prodash v19.0.1 ([`a0c88b7`](https://github.com/byron/prodash/commit/a0c88b7ce0541edd9b6f677454bc0fe694b16fc7)) - Line renderer will clear previous lines if progress is lost ([`dbca35f`](https://github.com/byron/prodash/commit/dbca35f478253176e852c177e79b3253eaafe3bd)) - Fix benchmark compilation ([`39bc237`](https://github.com/byron/prodash/commit/39bc2379c966244746b4ce361e576997159e19aa))
## 19.0.0 (2022-03-20) ### New Features - Improve render-log performance greatly. Previously it would check the current time each time somebody wants to log on any logger, greatly reducing performance as it would block on the mutex rust-std uses internally. Now we use a single thread to provide information about whether or not we may log, whose lifetime is bound to all of the log instances it governs. ### New Features (BREAKING) - Allow rendererers to respond to dropped progress roots Previously it needed extra effort to communicate that a the computation was done and the renderer should stop rendering progress. Now they only obtain a weak progress instance so it can drop if the computation is done, terminating it naturally and in time. Note that in case of the TUI, it would still be needed to respond to the GUI having shut down due to user request. ### Commit Statistics - 4 commits contributed to the release over the course of 41 calendar days. - 41 days passed between releases. - 2 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Release prodash v19.0.0 ([`fda577e`](https://github.com/byron/prodash/commit/fda577e72d831d455289a6786262ea94f861063e)) - Improve render-log performance greatly. ([`cba841c`](https://github.com/byron/prodash/commit/cba841c828142c0dd028dd9413c31f509f2bbb1b)) - Allow rendererers to respond to dropped progress roots ([`b6d5245`](https://github.com/byron/prodash/commit/b6d5245344bde92672cd98aecacb5d94ecca4e19)) - Actually, the correct dashmap version is 5.1 ([`6bdb7e8`](https://github.com/byron/prodash/commit/6bdb7e8ea3a65d51e69436799de3f9862b55dba4))
## 18.0.2 (2022-02-07) ### Chore - Upgrade dashmap to 5.0.1 (with security fix) ### Commit Statistics - 2 commits contributed to the release. - 5 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Release prodash v18.0.2 ([`69f4295`](https://github.com/byron/prodash/commit/69f42953ec69da4c2c34c8c137dfe7b7a1c12598)) - Upgrade dashmap to 5.0.1 (with security fix) ([`e4f2ab8`](https://github.com/byron/prodash/commit/e4f2ab842b34f4a4fe9b2f4c34b664a2e3dba200))
## 18.0.1 (2022-02-01) ### Bug Fixes - Downgrade to dashmap 4.0 While waiting for unoundness to be resolved. See the issue for details: https://github.com/xacrimon/dashmap/issues/167 ### Commit Statistics - 2 commits contributed to the release. - 9 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Release prodash v18.0.1 ([`e9769dd`](https://github.com/byron/prodash/commit/e9769dd7ad5c6f4618dd138aea74aba10fba69b3)) - Downgrade to dashmap 4.0 ([`a1f8aa6`](https://github.com/byron/prodash/commit/a1f8aa650d1a1d2ac53025e29c71782b1cab58c5))
## 18.0.0 (2022-01-23) ### New Features (BREAKING) - upgrade to tui 0.17 ### Commit Statistics - 3 commits contributed to the release. - 19 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Release prodash v18.0.0 ([`10531ae`](https://github.com/byron/prodash/commit/10531ae35570af6003275a92ba40612a5e23678f)) - Prepare changelog ([`a7a58c0`](https://github.com/byron/prodash/commit/a7a58c0fb6ea1068b914a653eb33c96dc157dc06)) - Upgrade to tui 0.17 ([`482b54f`](https://github.com/byron/prodash/commit/482b54f9c584b0e2d22c53622c9f7ad45b79ad2c))
## 17.0.0 (2022-01-03) ### New Features (BREAKING) - Add `MessageLevel` parameter to `Progress::show_throughput_with(…, level)` This allows to use message level for highlighting of certain throughputs and results. ### Commit Statistics - 2 commits contributed to the release. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Release prodash v17.0.0 ([`9085aaa`](https://github.com/byron/prodash/commit/9085aaaa0a7844c1d4b4afcec4b688f5c398ba87)) - Add `MessageLevel` parameter to `Progress::show_throughput_with(…, level)` ([`46214a3`](https://github.com/byron/prodash/commit/46214a306793a6a9f304f854dfd7396ceaf433d3))
## 16.1.3 (2022-01-03) ### Bug Fixes - `Progress::init(None, None)` now resets the progress entirely ### Commit Statistics - 2 commits contributed to the release. - 2 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Release prodash v16.1.3 ([`6551d45`](https://github.com/byron/prodash/commit/6551d45837aecc853f78e848b4257b559287a6ac)) - `Progress::init(None, None)` now resets the progress entirely ([`2fe3eeb`](https://github.com/byron/prodash/commit/2fe3eebbd62ddd9beacc294eb6ec04b4b39a26f6))
## 16.1.2 (2022-01-01) ### Bug Fixes - reset the shared value on init to avoid keeping the previously set value. ### Commit Statistics - 2 commits contributed to the release. - 4 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Release prodash v16.1.2 ([`3f16e6e`](https://github.com/byron/prodash/commit/3f16e6e31ee2b6df70841900d5b118f6533f9d2e)) - Reset the shared value on init to avoid keeping the previously set value. ([`aa70a27`](https://github.com/byron/prodash/commit/aa70a27ef1de930fdd00266239ee262b52a681a1))
## 16.1.1 (2021-12-27) ### Bug Fixes - correct signature of new 'running()' method ### Commit Statistics - 2 commits contributed to the release. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Release prodash v16.1.1 ([`f426c20`](https://github.com/byron/prodash/commit/f426c20dca5440f6d39c979a8a851ee444a2c388)) - Correct signature of new 'running()' method ([`ca5f544`](https://github.com/byron/prodash/commit/ca5f544594facc92c8744b293c7287dcffe065e5))
## 16.1.0 (2021-12-27) ### New Features - Setting the progress value is now 9x faster This is accomplished at the cost of not autoamtically setting the progress to 'running' anymore when the progress is set. ### Commit Statistics - 3 commits contributed to the release. - 8 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Release prodash v16.1.0 ([`34ae001`](https://github.com/byron/prodash/commit/34ae0014627c5fde785691ffc6583c52b629da51)) - Setting the progress value is now 9x faster ([`3886754`](https://github.com/byron/prodash/commit/3886754817ac528178b8ea326d0b4d576168eb28)) - An experiment to show we don't want to rely on dashmap for this ([`4f527c1`](https://github.com/byron/prodash/commit/4f527c12caa85018de293762194f9a2aed5daaea))
## 16.0.1 (2021-12-19) ### Chore - upgrade dashmap to latest version ### Commit Statistics - 6 commits contributed to the release over the course of 47 calendar days. - 109 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 1 unique issue was worked on: [#8](https://github.com/byron/prodash/issues/8) ### Thanks Clippy [Clippy](https://github.com/rust-lang/rust-clippy) helped 1 time to make code idiomatic. ### Commit Details
view details * **[#8](https://github.com/byron/prodash/issues/8)** - Run `cargo changelog --write` for an improved changelog ([`a1054e8`](https://github.com/byron/prodash/commit/a1054e89b6f5bbdead1a5c2f975cdce0da0700e7)) * **Uncategorized** - Release prodash v16.0.1 ([`9418df2`](https://github.com/byron/prodash/commit/9418df2660e5cb17c9906f86fb379c0d22c7ddb7)) - Upgrade dashmap to latest version ([`e6f53d5`](https://github.com/byron/prodash/commit/e6f53d59ef1aef027a2aad5b164535c6ca0d620b)) - Cleanup changelog ([`5aa6275`](https://github.com/byron/prodash/commit/5aa627523536a85c382f0da20636963387b437bd)) - Thanks clippy ([`c1258e2`](https://github.com/byron/prodash/commit/c1258e250207889c62ef3208590d84185752e1a2)) - Looks like array syntax isn't supported anymore ([`bfbce01`](https://github.com/byron/prodash/commit/bfbce01af9b90f5ae6b0490d6d7cdc29e05a1338))
## v16.0.0 (2021-08-31) ### Improvements - Use `time` version 0.3 when the `local-time` feature is enabled ### Breaking - rename cargo feature `localtime` to `local-time` - The `local-time` feature is not the default anymore, enable it using the `RUSTFLAGS="--cfg unsound_local_offset"` environment when building the binary. ### Commit Statistics - 4 commits contributed to the release. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Release prodash v16.0.0 ([`fec0304`](https://github.com/byron/prodash/commit/fec0304eba34db15c981a040c21e0371a5ac50d2)) - Improve docs ([`71d66ec`](https://github.com/byron/prodash/commit/71d66ecd2b3330759890f9056ea686c269fbe63a)) - Upgrade to time 0.3 and opt in to unsound features for example binaries (unix only) ([`fb3e0b0`](https://github.com/byron/prodash/commit/fb3e0b035cbe911d72a544a779d46cdda7b8105c)) - Rename 'localtime' to 'local-time' (cargo feature)… ([`b6ee809`](https://github.com/byron/prodash/commit/b6ee8096d22ce7a97c9bce9bcddad966a05b365f))
## v15.0.1 (2021-08-31) * crosstermion is optional for some renderers (again) ### Commit Statistics - 2 commits contributed to the release. - 25 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Release prodash v15.0.1 ([`72ea8a9`](https://github.com/byron/prodash/commit/72ea8a9565da457d6f1881bc64e6223c57a951a0)) - Fix manifest key! ([`d9275f2`](https://github.com/byron/prodash/commit/d9275f22c3845fb21f601d5d426a0949a398c47d))
## v15.0.0 (2021-08-05) * Upgrade to TUI v0.16 ### Commit Statistics - 4 commits contributed to the release over the course of 32 calendar days. - 50 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Thanks Clippy [Clippy](https://github.com/rust-lang/rust-clippy) helped 1 time to make code idiomatic. ### Commit Details
view details * **Uncategorized** - Upgrade to tui 0.16 ([`7b45023`](https://github.com/byron/prodash/commit/7b45023eb724429b2e0ba8794d7e80f867b51456)) - Thanks clippy ([`5ebf8b4`](https://github.com/byron/prodash/commit/5ebf8b4af0c19472a0efb29a2ac95dbe47c3fdd8)) - Dependency update ([`30d61c5`](https://github.com/byron/prodash/commit/30d61c5f57ab9d1200d237a23d272a43f1609956)) - Use pin instead of boxing unnecessarily. ([`aa715ae`](https://github.com/byron/prodash/commit/aa715ae0903cbc95a0e1245e5fa62e3b505eae35))
## v14.0.0 (2021-06-16) * Swap `ctrlc` crate with `signal-hook` which is a must in library crates. `ctrlc` is only for applications who can control the single handler that it installs entirely. ### Commit Statistics - 3 commits contributed to the release. - 38 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - (cargo-release) version 14.0.0 ([`a6492c6`](https://github.com/byron/prodash/commit/a6492c697f6afa79ee9bdec7c355d7f4388e5a6c)) - Prepare release ([`c2e3193`](https://github.com/byron/prodash/commit/c2e3193dab419c5bc94ee6138430248c0d36e299)) - Use signal-hook instead of ctrlc ([`a733267`](https://github.com/byron/prodash/commit/a7332677625fecf18566a05498b5e9bbd2108bc6))
## v13.1.1 (2021-05-08) * Fix compile error (and protect from that regression) if `render-line-autoconfigure` was enabled. ### Commit Statistics - 2 commits contributed to the release. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - (cargo-release) version 13.1.1 ([`d120f2f`](https://github.com/byron/prodash/commit/d120f2fb4f46fc66c4533d5c4f5f4ee1aeec7fd4)) - Fix compile issue, prep for patch release ([`584768a`](https://github.com/byron/prodash/commit/584768a07b37d6c11928dc022c44cf2d5c2c7e08))
## v13.1.0 (2021-05-08) * With the `render-line-autoconfigure` feature toggle, the new `Options::default().auto_configure(…)` method allows to adapt to the terminal/non-terminal autmatically. ### Commit Statistics - 3 commits contributed to the release. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - (cargo-release) version 13.1.0 ([`8aedcab`](https://github.com/byron/prodash/commit/8aedcab872bfbad27c4ca120370e428d1a227324)) - Update changelog ([`ffd4a1f`](https://github.com/byron/prodash/commit/ffd4a1f15a4ca0872f535fc2460890a9328ba7fd)) - Add render-line-autoconfigure feature toggle ([`212ce36`](https://github.com/byron/prodash/commit/212ce369c290a14e99be91e7d5cafb154154cf8b))
## v13.0.1 (2021-05-08) * The line renderer won't try to hide the cursor if the output isn't a terminal. ### Commit Statistics - 6 commits contributed to the release over the course of 5 calendar days. - 5 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Thanks Clippy [Clippy](https://github.com/rust-lang/rust-clippy) helped 1 time to make code idiomatic. ### Commit Details
view details * **Uncategorized** - (cargo-release) version 13.0.1 ([`0d41f11`](https://github.com/byron/prodash/commit/0d41f111875598a90b7a2a7a8f4e0872feb1a285)) - Prepare point release ([`8bfd42b`](https://github.com/byron/prodash/commit/8bfd42b0b03fc2b108ec68ea925e3d27742789da)) - Don't try to hide the cursor if the output isn't a terminal ([`26c7497`](https://github.com/byron/prodash/commit/26c74976ee4e6c3eec89dddf1a90e6ce22539dd8)) - More robust example for handling ttys and TUI ([`4d1dd31`](https://github.com/byron/prodash/commit/4d1dd313430054f17b2daddad8bd9d81061e60b0)) - Thanks clippy ([`13404f5`](https://github.com/byron/prodash/commit/13404f5f34d49e5607fa77e56bef0669b24c4adb)) - Remove crosstermion, it now lives in https://github.com/Byron/tui-crates ([`f560f84`](https://github.com/byron/prodash/commit/f560f84436c1bd2b5af8b0af44e8e86200d22cc2))
## v13.0.0 (2021-05-02) * Upgrade to TUI v0.15 ### Other - prep release - prepare release - Upgrade to tui 0.15 ### Commit Statistics - 13 commits contributed to the release over the course of 109 calendar days. - 110 days passed between releases. - 3 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - (cargo-release) version 13.0.0 ([`c0a97fa`](https://github.com/byron/prodash/commit/c0a97fa648cb2a0491d09bd0fbdd1daa6d6e290e)) - Prep release ([`e3665a2`](https://github.com/byron/prodash/commit/e3665a2100fba190fc0f047ff05f2904f4dcaf4a)) - Upgrade to tui 0.15 ([`edd11be`](https://github.com/byron/prodash/commit/edd11be0aa6ed84370603452286182a28c577961)) - Prepare release ([`c91d410`](https://github.com/byron/prodash/commit/c91d410e8d6242b78c44119155b5fc3b2956d111)) - Upgrade to tui 0.15 ([`03d1c20`](https://github.com/byron/prodash/commit/03d1c2067778fb6ec231bf18dd587046a03434bc)) - Revert "Allow most recent version of 'time' crate" ([`ee8ab91`](https://github.com/byron/prodash/commit/ee8ab91f74d6e598f59947d08405ca1f7fdb2785)) - Allow most recent version of 'time' crate ([`300aa7b`](https://github.com/byron/prodash/commit/300aa7b29ff6bad31197368340bebd2e2a5dfea0)) - Run actions on main ([`a2b3037`](https://github.com/byron/prodash/commit/a2b303730602c1b6e4a63475b134f87d640b6d2f)) - Make it more obvious what prodash actually is ([`5b862d5`](https://github.com/byron/prodash/commit/5b862d5e262cc08466afed235b862a6e79bffc8b)) - Fix compile warning ([`d48f3b9`](https://github.com/byron/prodash/commit/d48f3b97643cbd3a264400cacff29eb1436cb002)) - Use new resolver ([`ad03a43`](https://github.com/byron/prodash/commit/ad03a43056351899b8b76a62b0a0598b83c1e213)) - Fix compile warnings ([`4cb8681`](https://github.com/byron/prodash/commit/4cb8681f3826994d92b703cc7cf105ccf01cc4d8)) - Fix typo ([`75f311e`](https://github.com/byron/prodash/commit/75f311e5da2b5aeff608048d13075acb3dd41a0e))
## v12.0.2 (2021-01-12) ### Commit Statistics - 2 commits contributed to the release. - 3 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - (cargo-release) version 12.0.2 ([`29e16a4`](https://github.com/byron/prodash/commit/29e16a4faa4293f160b3294d327d2f7c4083ca2c)) - Add all missing docs to prodash ([`473c560`](https://github.com/byron/prodash/commit/473c56048e762ca9976260598005e1508e8d579c))
## v12.0.1 (2021-01-08) * upgrade dependencies ### Commit Statistics - 10 commits contributed to the release. - 4 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Thanks Clippy [Clippy](https://github.com/rust-lang/rust-clippy) helped 2 times to make code idiomatic. ### Commit Details
view details * **Uncategorized** - (cargo-release) version 12.0.1 ([`e93d001`](https://github.com/byron/prodash/commit/e93d00128cf24f63dcc18279bedea81d88732ef4)) - Prepare release ([`3e58d82`](https://github.com/byron/prodash/commit/3e58d826b855f6f10b7a787d34288f84eea35e5f)) - Fix localtime support ([`a3298d5`](https://github.com/byron/prodash/commit/a3298d5ab7e190d24ecdddc78f8c1cfcf5226bcb)) - Fix time offset calculations which can (and do) indeed fail ([`fed01f7`](https://github.com/byron/prodash/commit/fed01f7bccc234534631341706527a47d19a6dfa)) - Remove previous executor/reactor in favor of async-executor ([`1bea3cb`](https://github.com/byron/prodash/commit/1bea3cb16136c59bf9654fff16bcfefb1d0fa615)) - Use new spawn for simple example ([`9a8af0f`](https://github.com/byron/prodash/commit/9a8af0fe86246cbc99bd8440529975189e77cc00)) - Upgrade 'rand' ([`1c4930a`](https://github.com/byron/prodash/commit/1c4930af7e8960a94360f2ac6fdeae48920f7f93)) - Thanks clippy ([`644809a`](https://github.com/byron/prodash/commit/644809a7e40fc3b49116e0dfad718fc4fa850164)) - Upgrade dashmap and env_logger ([`cec8ab3`](https://github.com/byron/prodash/commit/cec8ab38513db79e588dc633218c565d5634f23f)) - Thanks clippy ([`32be130`](https://github.com/byron/prodash/commit/32be1300186fce4d543861b850ca7adf7d8c76e2))
## v12.0.0 (2021-01-04) * Upgrade to TUI v0.14 ### Commit Statistics - 2 commits contributed to the release. - 49 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Upgrade to tui 14 ([`06791b3`](https://github.com/byron/prodash/commit/06791b3cf0dd2589985bc1deb82b73e9278ae723)) - Update to tui 14 ([`169d62d`](https://github.com/byron/prodash/commit/169d62d04f50eb1b97e6766eb8e7a8f7878b2aef))
## v11.0.0 (2020-11-15) * Upgrade to TUI v0.13 and crossterm v0.18 ### Commit Statistics - 10 commits contributed to the release over the course of 47 calendar days. - 59 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Bump major version ([`b2989fa`](https://github.com/byron/prodash/commit/b2989faa799f27701193c52c1b335302f9751639)) - Upgrade to tui v0.13 ([`808f7d5`](https://github.com/byron/prodash/commit/808f7d5983ed9a3165afbd8f59959eca16d4a16a)) - Crosstermion now uses tui v0.13 ([`7684a50`](https://github.com/byron/prodash/commit/7684a50e1b94d6f19fa7d2c4d646ff569a51ffbd)) - Cargo clippy ([`725449b`](https://github.com/byron/prodash/commit/725449bfb3f4e39a543b023561b20365d9052a62)) - Update README.md ([`b485568`](https://github.com/byron/prodash/commit/b4855683b4d33c4db2eade4db7a33a53de47961a)) - Update README with accurate instructions on running example ([`d9288e9`](https://github.com/byron/prodash/commit/d9288e946bbc791f630c568b2b65899f644abd81)) - Ignore interrupts when reading inputs… ([`a3bf8be`](https://github.com/byron/prodash/commit/a3bf8beeb8f9496fa102ff59aeb4a7c6bdcac70e)) - Update to crossterm 0.18, but… ([`b5aa292`](https://github.com/byron/prodash/commit/b5aa292bd89cb223e9239bad3decc74495363b55)) - Update to tui 0.12 ([`a70e96d`](https://github.com/byron/prodash/commit/a70e96d64f52de0d3d4fcec9cbacbdf0fd6b7bb0)) - Upgrade to tui 0.12 ([`0606d46`](https://github.com/byron/prodash/commit/0606d4639c286c1917b85aee02f294dbadbaba77))
## v10.0.2 (2020-09-17) * Remove `futures-util` dependency ### Commit Statistics - 10 commits contributed to the release over the course of 3 calendar days. - 3 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - (cargo-release) version 10.0.2 ([`6e84aef`](https://github.com/byron/prodash/commit/6e84aef4df2d5d4c465abaa9b8a8a804f238e4ac)) - Changelog update ([`75258ea`](https://github.com/byron/prodash/commit/75258ea37c17975b2359ee0fdf34836430a7dd08)) - Remove dependency to futures-util ([`45b8dba`](https://github.com/byron/prodash/commit/45b8dbafd0d7e80d7169212ee4b1a0d696e8c4ec)) - Switch to release version of futures-lite ([`4fda4c0`](https://github.com/byron/prodash/commit/4fda4c05e5f4b7be2ff4a3b8898b060aa5c49ed4)) - Switch to latest version of futures-lite to remove stream::select_all ([`53ff638`](https://github.com/byron/prodash/commit/53ff638997736010f696db21c95c70cd87792ab0)) - Get rid of some more future-util functionality - just one more missing ([`0c82556`](https://github.com/byron/prodash/commit/0c82556cba25c16bd50c7abc39f8bb8078ea4228)) - (cargo-release) version 0.3.2 ([`e187a9d`](https://github.com/byron/prodash/commit/e187a9d4addcde2587f02fdffa1d7a06a58dd3a6)) - Crosstermion without futures-util! ([`d41352d`](https://github.com/byron/prodash/commit/d41352dff8954cf64ed31c2644aba5c4d846256b)) - (cargo-release) version 0.3.1 ([`0a6b6bc`](https://github.com/byron/prodash/commit/0a6b6bcb2ce629254e715ecdff5544303e7c2d2c)) - Upgrade futures-lite dependency ([`da021ea`](https://github.com/byron/prodash/commit/da021ea8306eaadb2bd40b155d4c431a164b6c42))
## v10.0.1 (2020-09-13) * upgrade dependencies to latest versions ### Commit Statistics - 5 commits contributed to the release. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Thanks Clippy [Clippy](https://github.com/rust-lang/rust-clippy) helped 1 time to make code idiomatic. ### Commit Details
view details * **Uncategorized** - (cargo-release) version 10.0.1 ([`a401d55`](https://github.com/byron/prodash/commit/a401d55dffd8191308dcee55c3404d6e0d777ccd)) - Thanks clippy ([`2ea963c`](https://github.com/byron/prodash/commit/2ea963cb7bf31f63fa7d64e7b6183f0cb73cbcc8)) - Upgrade blocking ([`01db9af`](https://github.com/byron/prodash/commit/01db9af60103abc1c2dbc486943c5fe9878abb1b)) - Upgrade async-io ([`4d71843`](https://github.com/byron/prodash/commit/4d71843120b68fa431628627b6b5458b3576b70a)) - Upgrade to latest futures-lite ([`9eb47a6`](https://github.com/byron/prodash/commit/9eb47a671ea8befb7f16d805d9c7c57f0c72d5c2))
## v10.0.0 (2020-09-13) ### Breaking * Enforce `Send + 'static` bounds for `Progress` trait. * This way it's clear that Progress is supposed to work in a threaded environment, which is the environment they are used in most often. * On the call site, this avoids having to specify these trait bounds explicitly. ### Commit Statistics - 1 commit contributed to the release. - 27 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Progress with 'Send + 'static' bounds; bump major version ([`50a90ec`](https://github.com/byron/prodash/commit/50a90ece86e9642cb8005a7b1472d29b4b14f197))
## v9.0.0 (2020-08-16) ### Breaking * add `set_name(…)` and `name()` to `Progress` trait. ### Commit Statistics - 1 commit contributed to the release. - 4 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Add 'name' and 'set_name' methods to trait ([`94c4390`](https://github.com/byron/prodash/commit/94c4390af8cf2ba5c9fa03d6177cff72cb762ad8))
## v8.0.1 (2020-08-11) Add missing trailing paranthesis in throughput display ### Commit Statistics - 1 commit contributed to the release. - 1 day passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Add missing parenthesis; bump patch level ([`b2af5b9`](https://github.com/byron/prodash/commit/b2af5b938d2690d2feed23e9d02ce683c280aceb))
## v8.0.0 (2020-08-10) ### New Features * Provide various Units for improved formatting of durations, steps, counts and bytes, and specify display of percentages. * Support for throughput display if enabled in _line_ and _tui_ renderer, and when opted in when creating units. * Turn `ProgressStep` type into usize to allow for a broader range of values. Specifically, this enables counting bytes read and written of files or streams bigger than 2^32-1 * A new example program: **units** * Add methods to help determine key hierarchy, useful for writing custom renderers: `Key::shares_parent_with(…)`,`Key::adjecency(…)` * `Key::adjecency(…)` ### Breaking * In `Progress::init(max, unit)` , `unit` now is `Option`, existing code can be transformed using `progress.init(None, Some("label".into()))`. * moved`tree::progress` into `progress` and renamed `Value` into `Task` and `Progress` into `Value` * moved`tree::messages` into `crates::messages` * moved`tree::key` into `crates::progress::key` * moved `tree::Throughput` into `crate::Throughput` * **removed** `deep_eq()` method in `Root` tree * tui engine option `redraw_only_on_state_change` was removed without substitute * **Move and rename** * `tree::ProgressState` → `progress::State` * `tree::Value` → `progress::Value` * `tree::Progress` → `Progress` * Remove `Hash` implementation for all public types except for `tree::Key` * Move `tui` and `line` renderers into the `render` module * Rename `log-renderer` feature to `progress-tree-log` * Rename `tui-renderer*` into `render-tui*` and `line-renderer*` into `render-line*` ### Other - Attempt to impl throughput in display… …which can't work because it's actually never mutable due to the way drawing work: it operates on a snapshot, a copy, that is not written back. And even if it was, the type system statically concludes sync is needed as well for this to work. Long story short: No state changes are ever allowed with a system like this, and throughput needs to maintain just that. Throughput must be implemented in each renderer. - Try to manually implement/run a local executor ### Commit Statistics - 91 commits contributed to the release over the course of 19 calendar days. - 19 days passed between releases. - 2 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Thanks Clippy [Clippy](https://github.com/rust-lang/rust-clippy) helped 2 times to make code idiomatic. ### Commit Details
view details * **Uncategorized** - More precise throughput computation ([`360d3ee`](https://github.com/byron/prodash/commit/360d3ee664b21c126b087ada5cd897927e0389bc)) - Remove throughput field which was effectively constant ([`52661ee`](https://github.com/byron/prodash/commit/52661eea38a511d3b1b47d63fd9ef9ac0f46caaf)) - ThroughputOnDrop utility ([`a4ea34c`](https://github.com/byron/prodash/commit/a4ea34c5a0f5370e7d109abccab3872c4f38aa8d)) - Provide a way to take out progress from a DropOrDiscard (for completeness) ([`2d57a54`](https://github.com/byron/prodash/commit/2d57a54c1508bc9314713384fe9cb08f949cbf76)) - Make throughput printing more flexible, allowing to override value and unit ([`a80104c`](https://github.com/byron/prodash/commit/a80104c955675c6cc40adcd8b90885010bfc9d0a)) - Format throughput utility with less arguments and using available Unit ([`0e84270`](https://github.com/byron/prodash/commit/0e8427029abb2d87244aa51669ea82086b545b81)) - Fix benchmarks ([`7c5be6b`](https://github.com/byron/prodash/commit/7c5be6b62a85d8b06d1f2609203e38173903e611)) - Make 'Log' more intuitive to reach ([`8cf7452`](https://github.com/byron/prodash/commit/8cf7452b795d0d1b7e16df75cf4a1cd4c3cbec3f)) - Convenience functions for nicer use of render() ([`4b396f5`](https://github.com/byron/prodash/commit/4b396f5f49d879006801cd678a17628ec56f3d19)) - Conform feature names for renderers ([`673d149`](https://github.com/byron/prodash/commit/673d149387b745944915ae1472e7a52bbd0d48de)) - Conform feature name ([`8ba1e96`](https://github.com/byron/prodash/commit/8ba1e96702bccb36886fe99bb0fa5eb7c58af7b9)) - Thanks clipppy ([`72bfa47`](https://github.com/byron/prodash/commit/72bfa47d468b2ecaf6086b62b2d1a25c860f1baa)) - Log progress now works with units ([`37f1266`](https://github.com/byron/prodash/commit/37f1266131bf52b35e5c473305f4c1d07df468f2)) - First bare addition of log progress (from gitoxide) ([`006ba9d`](https://github.com/byron/prodash/commit/006ba9db0345953ff2b76ec4f28f9b58a60c7ec1)) - Feature toggle for dashmap backed tree ([`fa688c8`](https://github.com/byron/prodash/commit/fa688c80e7ba264c5b87ae06b9aadebacb0d823e)) - Make tui renderer use Root trait ([`3da74b0`](https://github.com/byron/prodash/commit/3da74b0aff7c615911f0259336b7ec28a11249d1)) - Use Root trait in line renderer ([`a977c44`](https://github.com/byron/prodash/commit/a977c446534ea7c18f037e2a51dcbc419aac7c3d)) - Implement Root trait for tree::Root ([`ec5c673`](https://github.com/byron/prodash/commit/ec5c6732098f9f90ba62184aacdd76f397d3d8a8)) - Allow general use of Throughput by moving it into the top-level ([`cdabdce`](https://github.com/byron/prodash/commit/cdabdceb7ba9db76630f0cf58a7a4e2d4cc03f6f)) - Move `tree::key` into `progress::key` ([`d6f66b7`](https://github.com/byron/prodash/commit/d6f66b785a12f3972c29391df8a8d842cfed2a46)) - Refactor ([`63fd65a`](https://github.com/byron/prodash/commit/63fd65a82c57af25023ee9615662481b49e1d4e3)) - First stab at sketching out the Root trait - it needs a key, however :D ([`08d30c6`](https://github.com/byron/prodash/commit/08d30c61f12aa69a66e46639016e4c9c15300a20)) - Upgrade `init` to support the `Unit` type ([`226a849`](https://github.com/byron/prodash/commit/226a84944c8caba5aa7abac99e3d815b33d5f935)) - Add `Progress` trait and implement it for `tree::Item` ([`93ffb60`](https://github.com/byron/prodash/commit/93ffb6037bec833fdf2d0bc51bbe6632d06aa17a)) - Rename `Value` -> `Task` and `Progress` -> `Value` ([`f021ed8`](https://github.com/byron/prodash/commit/f021ed8f26faa360bfbd68b3a4479540dcea2942)) - Move `tree::messages` into `crate::messages` ([`4bce1d7`](https://github.com/byron/prodash/commit/4bce1d742874dd2708f04f544d2979d8084c4667)) - Moved tree::progress::* back into `crate::progress` ([`cf7405c`](https://github.com/byron/prodash/commit/cf7405c3d0ef6d6cbade68f68a1615a04e34bde8)) - Prepare for generalizing the root interface to keep renderers general… ([`4e38c6b`](https://github.com/byron/prodash/commit/4e38c6bb1ea157fb067dd61f015e2cb0a2855c70)) - Move most progress related types back to tree, however… ([`e6f242e`](https://github.com/byron/prodash/commit/e6f242e8555b2ef0fc15d6cf25273a991332f47c)) - Move line and tui renderers into `render` module ([`31358a7`](https://github.com/byron/prodash/commit/31358a777b577772b28b9f58ba797d41fe5f4b00)) - Thanks clippy ([`7435f35`](https://github.com/byron/prodash/commit/7435f35f9ddd88443eb87c0fe13256172d23d0f5)) - Disable throughput for dashboard as it's not reqired ([`be39f49`](https://github.com/byron/prodash/commit/be39f49fa2e84139c6af2e870dcf079b234cabe6)) - Remove Hash from types that don't need it ([`95e89ae`](https://github.com/byron/prodash/commit/95e89ae3ac18ca0e302b84695a1b6043ecbc0ee2)) - Now it works! ([`2e91ed2`](https://github.com/byron/prodash/commit/2e91ed217f241290ffcb36c77e178f26393df422)) - First attempt to bring throughput to TUI ([`d0e3af3`](https://github.com/byron/prodash/commit/d0e3af34ebf5f01bc1923e0024667200361a104b)) - Refactor ([`9212de4`](https://github.com/byron/prodash/commit/9212de4662f7e35492a8796d5ef4dcece91e5ea8)) - Throttling throughput recomputation to 1s really does the trick ([`6a4ae4e`](https://github.com/byron/prodash/commit/6a4ae4e7b933695e4c32359237c97cacc543ea8c)) - First working example for throughput, but… ([`0ceebd4`](https://github.com/byron/prodash/commit/0ceebd4fa0e3b25e15cbeb3b68dddc76b28515e2)) - First dummy throughput impl does what it should, nice! ([`e2ffb04`](https://github.com/byron/prodash/commit/e2ffb047b2eab3785b33db0925e7503d8e6300ea)) - Possibly working impl of getting the throughput + reconcile ([`74315bf`](https://github.com/byron/prodash/commit/74315bf28e8451ef8008c6b98630b8984db38464)) - Integrate calls to (optional) throughput into line renderer ([`a1af8fc`](https://github.com/byron/prodash/commit/a1af8fc78b7e5f8fd49b28df6352e9690caed8af)) - First sketch of Thoughput handler ([`2edeefc`](https://github.com/byron/prodash/commit/2edeefca27592583186bca914b50e4bf6fc5f7e7)) - Refactor ([`80684b3`](https://github.com/byron/prodash/commit/80684b3b60b069a6942b790c9b308649f432dbc4)) - Refactor ([`19e0901`](https://github.com/byron/prodash/commit/19e090164be073351b16ded0a75168b48a5cf654)) - Refactor ([`0402040`](https://github.com/byron/prodash/commit/040204078802f1d97a1a186f75d2e61cda3dd5d5)) - Refactor ([`b981d2e`](https://github.com/byron/prodash/commit/b981d2eb055248ee67ada33da3b4ef946a842e8d)) - Make key adjecency helpers public ([`3b12ea2`](https://github.com/byron/prodash/commit/3b12ea292468ed745d54267b7635b6b67ea84195)) - Thanks clippy ([`a99e791`](https://github.com/byron/prodash/commit/a99e791427b5e2af4ad421545a650ca03c8f4e6c)) - Refactor ([`c6068c5`](https://github.com/byron/prodash/commit/c6068c58bbcbf8faa28d9dc7d5ec3766ec41d953)) - Refactor ([`983d2e5`](https://github.com/byron/prodash/commit/983d2e5c90773694dcd42fa54b5574a61621cf44)) - Refactor ([`0c537ab`](https://github.com/byron/prodash/commit/0c537ab65af9d388e02fdea7175e7fbcd4fb4a3e)) - Basic display of timespans for throughputs ([`fd68710`](https://github.com/byron/prodash/commit/fd687101407eced316b544dff48bb914acc5cf7f)) - Refactor ([`40869a8`](https://github.com/byron/prodash/commit/40869a8322faa352c0d082f7ee33c2e533d9836d)) - Prepare to move throughput calculations into renderer ([`682dee2`](https://github.com/byron/prodash/commit/682dee2ad35766c56e01458d0ec151587079ee4a)) - Attempt to impl throughput in display… ([`66800fd`](https://github.com/byron/prodash/commit/66800fd4e6c9f517f19da4e26a75cb3f139353b0)) - Pass elapsed time on in tui renderer ([`eb22417`](https://github.com/byron/prodash/commit/eb224176f041de36f9a6ab42888bceef0d3b85c5)) - Pass elapsed time on in line renderer ([`33be555`](https://github.com/byron/prodash/commit/33be555f1e6c5141c5ee756a1d42b7ea0762f975)) - Allow providing an optional elapsed duration… ([`a4b4ab7`](https://github.com/byron/prodash/commit/a4b4ab7d95457f7b662fb98d12eefb2bd0c34f39)) - First test for throughput - before major refactoring ([`963c933`](https://github.com/byron/prodash/commit/963c933ebd2f5dcb090460dc79c445e122731c9a)) - Remove unnecessary tui option: redraw_only_on_state_change ([`f78cf4f`](https://github.com/byron/prodash/commit/f78cf4fe8740b55f5c363151eab2ee1da558390c)) - Prepare throughput display ([`3266fad`](https://github.com/byron/prodash/commit/3266fad2ad5e63855eb92c1f0c390bf2bfd8167f)) - Make Unit a struct, introduce new 'Kind' to capture dynamic and static strings of display value ([`b413111`](https://github.com/byron/prodash/commit/b41311114ce3be7174dea7c1751fb15b20284794)) - Prepare Mode to carry information about throughput handling ([`ea705ac`](https://github.com/byron/prodash/commit/ea705ac4089d7958e97b81f3ee1f8261e9948d2f)) - Fix benches ([`a2d35fb`](https://github.com/byron/prodash/commit/a2d35fb096a915c0a55985f619a12149add29251)) - Finish simple units example, showing all available units ([`de6addd`](https://github.com/byron/prodash/commit/de6addde16da64a7884eca8de221d398d868171c)) - First simple progress bar using bytes ([`e3bdbf1`](https://github.com/byron/prodash/commit/e3bdbf1a8ef5c132405ec6570422b87ed07360ce)) - Frame for new example program ([`2f1cb12`](https://github.com/byron/prodash/commit/2f1cb125b455c7357fd6576f3180e145c5efdc44)) - Finally run unit-tests as well ([`70d7ae2`](https://github.com/byron/prodash/commit/70d7ae2bcdf98e60f83286cf12e53e0690893eef)) - Refactor ([`6a18584`](https://github.com/byron/prodash/commit/6a185843344c1f54efc70f8bcf0e3736b6be87b0)) - Use new Unit type everywhere ([`539edde`](https://github.com/byron/prodash/commit/539eddecb9d91d315771c8f30339c3b5cc186e18)) - Support for nicer duration display ([`d500412`](https://github.com/byron/prodash/commit/d500412766c850133569c200cc13f6040a665af2)) - Integrate humantime ([`f0f55bb`](https://github.com/byron/prodash/commit/f0f55bb6660523fe02cddc764a79fe2e3b459ce7)) - Make range more compliant to aid consistent coloring ([`afb0c91`](https://github.com/byron/prodash/commit/afb0c91a20bd187f271791056238b0563015fbd1)) - A new range display mode, only good with an upper bound ([`b83d6bd`](https://github.com/byron/prodash/commit/b83d6bd7137ea0dc8e9731552ae125cec6cb8631)) - Support for omitting the unit ([`f350079`](https://github.com/byron/prodash/commit/f350079a9db9433936f0f90fe9316d0bed65cf38)) - Using fmt::Write is so much better! ([`bf89a3d`](https://github.com/byron/prodash/commit/bf89a3d11fcc6eba087da1d28cace7991156bb39)) - Trying to use byte-size shows that the trait interface isn't flexible enough ([`f233d43`](https://github.com/byron/prodash/commit/f233d43f2d48bb911cb20808396d4a2b0d55058d)) - Allow splitting up generation of values and unit for more creative control… ([`0e15b97`](https://github.com/byron/prodash/commit/0e15b97d6a7cf5dcd7be068de8bf3f4b6472089c)) - Percentage support ([`43c0980`](https://github.com/byron/prodash/commit/43c0980ffe15efe5e4994398d16b519d9e19836e)) - First small steps towards implementing unit and value display correctly ([`5bd90cc`](https://github.com/byron/prodash/commit/5bd90cc6539bf61019de6d096589b1c2caeb685e)) - Refactor ([`716e774`](https://github.com/byron/prodash/commit/716e77489aedd9df54745793c979af67acfbb41f)) - Refactor in preparation for another example application ([`6063c27`](https://github.com/byron/prodash/commit/6063c2796b0b2508a73eb41b0c6c9ed8a601e21b)) - Rough layout on how to get much more powerful unit and value rendering ([`90a9c2d`](https://github.com/byron/prodash/commit/90a9c2dac084b5ee9a60065875401c57644dce00)) - First sketch for new Unit type and trait to display what we are interested in ([`129d09d`](https://github.com/byron/prodash/commit/129d09d6190619e64144a93d617b65f434d26f50)) - Use 'usize' as ProgressStep, instead of u32 ([`79ae31f`](https://github.com/byron/prodash/commit/79ae31fc1dde5120a6e2706bc84995f72cc587dd)) - Line renderer: more compact progress bar ([`8071110`](https://github.com/byron/prodash/commit/8071110e8a41612808206c3f8f444542818f4a51)) - Revert "FAIL: Try to manually implement/run a local executor" ([`f6fa7ab`](https://github.com/byron/prodash/commit/f6fa7ab681549abfd77a14e4c8015f623f720c83)) - Revert "Another attempt of using a local executor: FAIL" ([`a3254a6`](https://github.com/byron/prodash/commit/a3254a6aac6b4150bcbd10004d55d4cc06d45dbe)) - Another attempt of using a local executor: FAIL ([`ee6275c`](https://github.com/byron/prodash/commit/ee6275ca3f0da906b9f94e6d95d9ce130907e9c8)) - Try to manually implement/run a local executor ([`64cfe9e`](https://github.com/byron/prodash/commit/64cfe9e87e038fb36492307dfb75cbc8204180d8)) - Actually only a few lines are needed to drive multi-task ([`d19a1db`](https://github.com/byron/prodash/commit/d19a1db08305abb77505868cc237b1c71491960d))
## v7.1.1 (2020-07-22) * dependency update: smol 0.2 ### Commit Statistics - 2 commits contributed to the release. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Patch bump ([`33f57ef`](https://github.com/byron/prodash/commit/33f57efcd25523318d4c494dfd29d538be964992)) - Upgrade to smol 2.0 ([`ab7dcb4`](https://github.com/byron/prodash/commit/ab7dcb4f2b108fbf7f525b61110f3f6b6339d0b0))
## v7.1.0 (2020-07-22) * Improved looks thanks to bold fonts ### Commit Statistics - 5 commits contributed to the release. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Bump minor version ([`aac8cb2`](https://github.com/byron/prodash/commit/aac8cb2ce99c3eb70b42919754e6ecefcab43854)) - Use bold characters for some highlights ([`56cf67e`](https://github.com/byron/prodash/commit/56cf67e36ed0db6a4a78e1d30e7fc4eebe7c7342)) - Upgrade to tui 0.10 ([`79afe12`](https://github.com/byron/prodash/commit/79afe123dda6e892425772fca8e040ac256edf9d)) - Crosstermion 0.3 with support for tui 0.10 ([`dcda91b`](https://github.com/byron/prodash/commit/dcda91b744cb9f4f735591a53997ac4f2747a61e)) - Decouple prodash from local crosstermion for tui migration ([`1105cfd`](https://github.com/byron/prodash/commit/1105cfd46d9a163c6e6d6c761fec8f70a0b08156))
## v7.0.4 (2020-07-21) * **tree::Item** * Add new methods `inc_by(step)` and `inc()` for convenience * **line renderer** * They now look clearer, as they changed from \[===> ] to \[===>------] ### Commit Statistics - 7 commits contributed to the release over the course of 1 calendar day. - 9 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - (cargo-release) version 7.0.4 ([`45a8a00`](https://github.com/byron/prodash/commit/45a8a00a76c3beed0baf0cffaa5656f0835a927e)) - Improve line progress rendering ([`f694bb9`](https://github.com/byron/prodash/commit/f694bb995d6b3f3ef9c25b584bbd59bf7b625c7c)) - Add convenience methods to tree::Item ([`3a36ce6`](https://github.com/byron/prodash/commit/3a36ce6082b3c6d24c8196ed4e9c537a5f36cb4f)) - Prioritize the tui engine for futures-lite ([`c17235c`](https://github.com/byron/prodash/commit/c17235c493fffb628b15a730aa72cdd39f93ca12)) - Attempt to switch to futures-lite, but a few things are missing ([`c8e52c2`](https://github.com/byron/prodash/commit/c8e52c2c045ddcd33f6f40d3f56ffbb3d00eb012)) - Remove unused dependency ([`85c1f6d`](https://github.com/byron/prodash/commit/85c1f6d894f651a006793dfa0df3a547c8ed4a29)) - Remove futures-util nearly completely - missing filter_map() extension for stream ([`6fc5846`](https://github.com/byron/prodash/commit/6fc58461028e63ca333adf7381178473329fe643))
## v7.0.3 (2020-07-11) cleanup and code simplification in the line renderer. ### Commit Statistics - 3 commits contributed to the release. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Bump patch level ([`ffc58bb`](https://github.com/byron/prodash/commit/ffc58bbd0d305b4e3c1b33f5a6661cea4b692def)) - Crosstermion 0.2 with much less code and complexity ([`f3b24d0`](https://github.com/byron/prodash/commit/f3b24d0edfdc40c5fb045facd467aab2e7e53f12)) - Greatly simplify line renderer engine code ([`f0eaf27`](https://github.com/byron/prodash/commit/f0eaf279c42f0a7d28bc6b4c6d064df072f3a3a7))
## v7.0.2 (2020-07-11) * **render-line** `JoinHandle` will * now send a signal to perform a render before shutting down to capture the final state * wait for the render thread to complete the aforementioned actions on drop. You can still override this behaviour through `disconnect()` or `forget()`. * removed special code-paths that avoided bringing up another thread for 'ticks' at the expense of shutdown delay. ### Commit Statistics - 2 commits contributed to the release. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Bump patch level ([`9a7c2ef`](https://github.com/byron/prodash/commit/9a7c2efec958c5b0729e799177a94f5089882158)) - Various improvements to help integrating with the line renderer ([`3711394`](https://github.com/byron/prodash/commit/371139409619f4a3195aaeabc8bf38a3b3ec6209))
## v7.0.1 (2020-07-10) Prevent cursor movement if no progress bar is drawn. ### Commit Statistics - 3 commits contributed to the release. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Bump patch level ([`b1ca7fa`](https://github.com/byron/prodash/commit/b1ca7fa48d35a325566c90f49382b9385580be02)) - Don't cause cursor movement when using '0' as MoveUp value ([`d090c0c`](https://github.com/byron/prodash/commit/d090c0ccbce3c4323eb03dc31fc550b7c11f2cf2)) - Add new asciicast ([`443702b`](https://github.com/byron/prodash/commit/443702bf55c7e2677f867e1c5557dac0de78998b))
## v7.0.0 (2020-07-10) Add new render-line, change feature flag names. ### Other - first version of 'slow' event loop which actually won't respond quickly either :D - bump patch level ### New Features * **line** There is a new line renderer as neat trade off between bare logs and full-blown tui. It will work best with 'simple' and not too dynamically changing progress trees. Activate it with the `render-line` + one of `render-line-crossterm` or `render-line-termion` feature flags. * Activate it with the `render-line` + one of `render-line-crossterm` or `render-line-termion` feature flags. ### Breaking Changes * **`tui` module** * **TuiOptions** -> **Options** * `render_with_input` now takes the Write stream as argument, instead of defaulting to `std::io::stdout()` * **Feature Flags** * **with-crossterm** -> **render-tui-crossterm** * **with-termion** -> **render-tui-termion** ### Commit Statistics - 81 commits contributed to the release over the course of 4 calendar days. - 4 days passed between releases. - 2 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Minior cleanup ([`48879f3`](https://github.com/byron/prodash/commit/48879f3cf44d1b8531819f7c4d3ca84d0bf116e9)) - Dynamically resize and align progress bars - looks so much better now! ([`021dc5a`](https://github.com/byron/prodash/commit/021dc5abda66dad0c2270e5db093b3772e56e7b3)) - Dynamically recompute dimensions of log messages ([`5cb7fea`](https://github.com/byron/prodash/commit/5cb7fea8e60b829ea32fec1c1a161a3cddb7cb5e)) - Pass entire terminal dimensions to line buffer… ([`03a859c`](https://github.com/byron/prodash/commit/03a859c09f217c0d8e39b0ba7d2f05429fd92b16)) - Allow setting the terminal width using CLI in dashboard example ([`bfd1a98`](https://github.com/byron/prodash/commit/bfd1a98990f9c7628bd82d6c1391dca8f783f570)) - Always show log messages, but show progress only after initial delay. ([`7ec5530`](https://github.com/byron/prodash/commit/7ec5530ceed8c6b32b0a82fbe0f2d5e986d57a20)) - Initial progress messages should actually be shown despite initial delay ([`4d68060`](https://github.com/byron/prodash/commit/4d6806028a780633e24966ca9900e0a517e6b783)) - More fine-grained control over the amount of work displayed ([`9f4fa49`](https://github.com/byron/prodash/commit/9f4fa4938fac88ac2b70ebb96b52ddf7152cbd04)) - Progress drawing now can be without color as well ([`f980a83`](https://github.com/byron/prodash/commit/f980a83226489dc1a1066a488aa39eb3c4d54cba)) - Release notes ([`f1280a9`](https://github.com/byron/prodash/commit/f1280a9e18d3fc530cf7eb42eeb834d986c91477)) - Nobody needs ticks right now, and using the step for unbounded progress rocks… ([`298afe6`](https://github.com/byron/prodash/commit/298afe685589827ab9a882f37ee53726486bd678)) - Ascii-only unbounded progress ([`ab93db2`](https://github.com/byron/prodash/commit/ab93db261ec994ea8874c09015a1031ed91b70a1)) - Refactor ([`a4aa7e8`](https://github.com/byron/prodash/commit/a4aa7e8fb894be910d0e297eb1be6c495641fd9f)) - Reverse direction of unbounded progress by reversing an iterator, nice! ([`380330d`](https://github.com/byron/prodash/commit/380330d1e97011bc0ca1668d4686691b4a3986be)) - A first, but backward, version of unbounded progress ([`89fb23c`](https://github.com/byron/prodash/commit/89fb23ca701eefa8fac9bb54697050be0ea30d28)) - A somewhat usable display of bounded progress ([`8ebde51`](https://github.com/byron/prodash/commit/8ebde51451792a0b45080e364db7b840bece0282)) - Prepare nicer but non-unicode drawing to realize we really want to configure the width ([`54e1ea7`](https://github.com/byron/prodash/commit/54e1ea77395d1da7a4bb5e91c47cc5949282780c)) - Overdraw for log lines - works perfectly! ([`91ea381`](https://github.com/byron/prodash/commit/91ea381f8edbe9bdf2c19a2f0fdb5d4064bc19a3)) - Make 'hiding the cursor' configurable ([`03a8a9e`](https://github.com/byron/prodash/commit/03a8a9ee9236a53deafdd341cd2dce18e32c05a8)) - Fix 'make check' ([`a7bff83`](https://github.com/byron/prodash/commit/a7bff8372416f10d0ecb4175d20cb5b08529fe13)) - Refactor ([`3a1b6c5`](https://github.com/byron/prodash/commit/3a1b6c5726c12a0051e255563aeb822af298f98d)) - Refactor ([`fb91dde`](https://github.com/byron/prodash/commit/fb91dde5cf89d3efc44d554ee1eb433d72b5d542)) - Show and hide the cursor if the ctrl+c is pressed (or SIG_TERM is sent) ([`ff98952`](https://github.com/byron/prodash/commit/ff98952c1f0310d32e580d5ba56d4615b64a24ed)) - Refactor ([`dcb24ea`](https://github.com/byron/prodash/commit/dcb24eaac1f31bb07bc3fea5a8893018c7b220b6)) - Use a vecdeque as it is just perfect for what we need to do here ([`8b7dbaa`](https://github.com/byron/prodash/commit/8b7dbaa9cbd2b712ed98f9af5c0ed897f95f0ebd)) - Basis for overdraw of log messages ([`7a4aecf`](https://github.com/byron/prodash/commit/7a4aecfef256ee4d4a21059a2ef35b010d3514e3)) - Obtain terminal size from crossterm or termion, now the dashboard… ([`3484fed`](https://github.com/byron/prodash/commit/3484fedd0a7b20b065edda3c101b31b2f22fd880)) - Support for termion version of the dashboard ([`a37cb2a`](https://github.com/byron/prodash/commit/a37cb2a03f832ee05c092c5bec0b481c1d922440)) - Incorporate filtering into the line count logic - that works actually ([`e38e559`](https://github.com/byron/prodash/commit/e38e55954d3bfcb016e23dac4f24b866e59aa302)) - Fix select issue, surprise. Crossbeam channels are certainly more robust ([`5be4885`](https://github.com/byron/prodash/commit/5be4885816d2ec2fccf2a0d2a01deb38e740edc4)) - Make interval ticker actually work - looks like flume can't always select ([`af710d4`](https://github.com/byron/prodash/commit/af710d4391d8103ee03dd385285159bd1d61eff3)) - Quite a failed attempt to move cursor back up for overdrawing… ([`04c686b`](https://github.com/byron/prodash/commit/04c686b6bec543e290b729b61ac69245953a4564)) - First rough drawing logic without cursor movement ([`0134e0d`](https://github.com/byron/prodash/commit/0134e0d54357d4a541555d5749e91b55ede7a692)) - Manually adjust fill length for correct results even with Chinese ([`4da38f2`](https://github.com/byron/prodash/commit/4da38f270c713ed2f6254154e06e655eee4dbae5)) - Try to align messages a bit more nicely, but… ([`2744e64`](https://github.com/byron/prodash/commit/2744e643d91ec411814df84dd755a9ffd304a9c1)) - Turn off timestamps by default ([`dd02770`](https://github.com/byron/prodash/commit/dd02770db79fc104b62a727cdf79266ca6f11298)) - Draw time as well in line renderer ([`fa51fad`](https://github.com/byron/prodash/commit/fa51fad3e0467f25d949c1dfb02cb38d10bb322f)) - Revert "Add time support to crosstermion - seems a bit out of place actually" ([`93397c7`](https://github.com/byron/prodash/commit/93397c7f89e9c3d790cf2552690de1ef66f9e2d1)) - Make 'time' module available to be shared by multiple renderers ([`c31d1c5`](https://github.com/byron/prodash/commit/c31d1c5adf6f83d0ffd44d91d8032f835038bb60)) - Add time support to crosstermion - seems a bit out of place actually ([`8e2bf7c`](https://github.com/byron/prodash/commit/8e2bf7cceb4f96d9fad523944868102951be094d)) - Move conditional painting code into crosstermion - could be useful for dua as well ([`6d04514`](https://github.com/byron/prodash/commit/6d04514d9015d6dcfec7688d6deab06b6ac33f54)) - First sketch of conditional drawing of log messages ([`78f56c0`](https://github.com/byron/prodash/commit/78f56c0ffa4bea77a6ab7458734ad2cd206ac3e8)) - Add a new 'color' module for crosstermion ([`272a852`](https://github.com/byron/prodash/commit/272a8525f5a102680f620d31d953467beae0b485)) - Fix division by zero when copying the entire message buffer ([`60f1bb8`](https://github.com/byron/prodash/commit/60f1bb8cb47b6f0720b064db811416eea52313bb)) - Cargo clippy ([`20f3144`](https://github.com/byron/prodash/commit/20f31449ae8e0c9de8c72f9286a7174a05972585)) - Add support for no-line-color flag; implement additional line renderer options ([`eda7fb3`](https://github.com/byron/prodash/commit/eda7fb32c8c621545824ecb7c37e7bfdd96cb3d3)) - Make coloring configurable, following some informal specs ([`1e1a02a`](https://github.com/byron/prodash/commit/1e1a02ab2c82ab332d5cbc8d5966810574cced8d)) - Integrate message copying into line renderer ([`79efb09`](https://github.com/byron/prodash/commit/79efb094c92bb99eae77391dd3ffc3551c79102b)) - Copying only new messages seems to work now ([`982dfee`](https://github.com/byron/prodash/commit/982dfeedb10d32d469fc3424b74d9629a82c3cd5)) - Refactor ([`3bb08f7`](https://github.com/byron/prodash/commit/3bb08f707503c802a234c18e03af7ebcd578dff4)) - Initial mostly working version of copying only new messages from the buffer. ([`d78472c`](https://github.com/byron/prodash/commit/d78472c579bfa20f4b2cc3fd2a062dac330dfdbf)) - Allow for a little more space when formatting :) ([`4bf4431`](https://github.com/byron/prodash/commit/4bf44310c9e1c37644e07092e1c5c80c9e6c45d4)) - First beginnings of testing copy_new(…) ([`917dd05`](https://github.com/byron/prodash/commit/917dd05122c39cd065eb5b7b761d083bc003a86f)) - Now actually fix the 'copy_all(…)' method for the message buffer :D ([`2bb088d`](https://github.com/byron/prodash/commit/2bb088d48408dc2106dbe5ad2bed8db5699bba15)) - Sketch for stateful message copying to copy only new ones. ([`90750c7`](https://github.com/byron/prodash/commit/90750c759a76d08b40b5e1b26d0d8d975afc1e2a)) - Fix off-by-one error in messsage buffer copy handling ([`c6e1ece`](https://github.com/byron/prodash/commit/c6e1ecefff385e4acc56047654a527316f7fd5ac)) - Refactor ([`ccc4297`](https://github.com/byron/prodash/commit/ccc4297a0e5dadca81a8398ae6495abbbf83cdd9)) - Refactor ([`ed338e0`](https://github.com/byron/prodash/commit/ed338e05c97085e0e7673d148f6d7f382a88ef03)) - Flesh out join handle with complete and symmetric API ([`1bd4476`](https://github.com/byron/prodash/commit/1bd4476b532425f0b7b2fdc82f058ed8b6b8317a)) - Support for interruptable initial delay ([`c15af5e`](https://github.com/byron/prodash/commit/c15af5ef2fe7c1bfde74f6d183c22688f666f398)) - Dashboard can now bring up the line renderer ([`f20f002`](https://github.com/byron/prodash/commit/f20f00282836f33465a372bf42ced1b04a3ca064)) - Don't assume quitting is requested on channel disconnect; allow detaching the handle ([`d050243`](https://github.com/byron/prodash/commit/d050243e8176a35ec29905a047e1a69b44ddf0e0)) - Frame to allow using 'line' renderer in dashboard example ([`4566e46`](https://github.com/byron/prodash/commit/4566e46155656e573af80fabd9df0ab6aec95531)) - Make dashboard depend on line renderer ([`ea861a2`](https://github.com/byron/prodash/commit/ea861a26e3ca8f6db37a0da79465531c2299c926)) - Now we are talking: Selector functions shouldn't have side-effects, 'wait()' is exactly it ([`9ebbc16`](https://github.com/byron/prodash/commit/9ebbc1685e4f7802cb6384711a06199d9cd8a58a)) - First version of 'slow' event loop which actually won't respond quickly either :D ([`a684188`](https://github.com/byron/prodash/commit/a684188b3eee0cc67fe48b9ae14aa9cd63603caf)) - Sketch draw logic for draw loops that are fast enough ([`b4db64e`](https://github.com/byron/prodash/commit/b4db64e3a134cea153db92ccb19dbc20d9ab9ee0)) - Sketch initial interface for line renderer ([`1712221`](https://github.com/byron/prodash/commit/1712221f04efb20717dbe662a8d29d4a23235989)) - Move changelog information into its own file ([`5aa3034`](https://github.com/byron/prodash/commit/5aa3034a2085fdf602641c7899448e30941183be)) - Prepare arrival of the mighty line-renderer feature :D ([`511389e`](https://github.com/byron/prodash/commit/511389e7491d11e06815b86f41b594a9df7c0529)) - Make external crates available as re-exports ([`0b8a763`](https://github.com/byron/prodash/commit/0b8a763585d7828f6ac1ccbcc23d985ff1eab3a9)) - Notes about the tradeoffs in backend choice ([`a942e85`](https://github.com/byron/prodash/commit/a942e85b62118c447214f683f3c866e502842337)) - Write down insights about coloring in terminals, in conjunction with crosstermion ([`c96abdb`](https://github.com/byron/prodash/commit/c96abdbc116801eb873e021151a853599f32ec43)) - Bump patch level ([`c879dfa`](https://github.com/byron/prodash/commit/c879dfa59fa4de6f7b0a02de42668224d791e5db)) - Fix description of crosstermion ([`8781c9f`](https://github.com/byron/prodash/commit/8781c9f3f70e12d436c957834f0a6dd61a74651b)) - Fix precedence in terminal module, crossterm is winning over termion now ([`521dd23`](https://github.com/byron/prodash/commit/521dd23dc67d6dee52cfbe0c35c3fe8fc28dc881)) - Bump patch level ([`1bc5c76`](https://github.com/byron/prodash/commit/1bc5c764c9b1190f168d076b2183a27569750421)) - Make sure conversions are always compiled ([`5a03628`](https://github.com/byron/prodash/commit/5a03628b1cafd8d06476f364242d0e149f4fad18)) - Fix flume features, bump to 0.1.2 ([`11d6665`](https://github.com/byron/prodash/commit/11d6665c77a1e4ce4dc262744ffb76dc8b907832)) - Bump patch level; add 'input-thread-flume' support ([`7fdbb72`](https://github.com/byron/prodash/commit/7fdbb72822c450a4e13eba998236b608cf9eeca3)) - Fix Cargo.toml to allow 'cargo test' to work without specifying features ([`748ab4b`](https://github.com/byron/prodash/commit/748ab4be6aa5fc975fcdcacbd92a2fa388103734))
## v6.0.0 (2020-07-05) Factor terminal input into the new `crosstermion` crate. Due to this work, the default features changed, which is a breaking change for those who relied on it. Now when using the `render-tui`, one will also have to specify either the `with-crossbeam` or `render-tui-termion` feature. ### Other - Add Key input transformation ### Commit Statistics - 24 commits contributed to the release. - 2 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Allow choosing the backend for the dashboard example, but people also have to chose is now ([`4841a8e`](https://github.com/byron/prodash/commit/4841a8ed3f949797990b0ad3a6148af4beb7203d)) - Release notes for v6.0 ([`403d5a0`](https://github.com/byron/prodash/commit/403d5a0af5fbd072b876c61d59cbaba8c261b7df)) - Fix crosstermion docs ([`71e1eca`](https://github.com/byron/prodash/commit/71e1eca154ccce82a012932c6b5f47cea57f4b9b)) - Make feature combinations for properly, but… ([`93a069a`](https://github.com/byron/prodash/commit/93a069a2e6d9757f6ca2b77300f8c7d7baf091d4)) - Cleanup; allow 'input-thread' and 'input-async' at the same time ([`93a30e0`](https://github.com/byron/prodash/commit/93a30e0baeeb59ea1b8f38e4a23c080c3942525f)) - Cleaner input handling using 'input-threaded' and 'input-async' ([`3d11e90`](https://github.com/byron/prodash/commit/3d11e9040510ccbc43057432c9e537c49fd8025d)) - Support for async crossterm in crosstermion ([`1a61453`](https://github.com/byron/prodash/commit/1a61453dd6e8f4c2eef7695e1e92285d318e9d9e)) - Use stream trait instead of channel directly to abstract a little ([`78e810d`](https://github.com/byron/prodash/commit/78e810d4ee0cb691cec3666f4c585fffd6d6481b)) - Don't continue the loop if sending fails… ([`e93fe6e`](https://github.com/byron/prodash/commit/e93fe6ebdf17abf38bba3e00a4c26ff822d42219)) - Switch from smol to futures-executor, it's probably cheaper for what we need ([`d7deeb7`](https://github.com/byron/prodash/commit/d7deeb71983d08cb5998e22f23482a0776b7fb2f)) - Cleanup, remove unused deps ([`050a821`](https://github.com/byron/prodash/commit/050a821a4f6d70ba624afdd48b744a62cb374254)) - Fix prodash build after function renmae ([`3848635`](https://github.com/byron/prodash/commit/3848635432177f5cbc7ac9f18e7c9deadfeaf588)) - Docs for crosstermion ([`3429327`](https://github.com/byron/prodash/commit/342932702dfc2ce359548eef72ff86d0b9030fbb)) - Enforce testing termion on linux ([`0cce091`](https://github.com/byron/prodash/commit/0cce091c56fcda36a1ca5eb918bb67e48db6f2d9)) - Prodash now uses `crosstermion` ([`97ad454`](https://github.com/byron/prodash/commit/97ad45420ad569682e3ee62d7bb7c28fe287b9e3)) - Bundle-features and feature documentation ([`b66c413`](https://github.com/byron/prodash/commit/b66c4133fdec9b3cfc5a290898de9ce71f22e10c)) - Marry raw mode with the alternative terminal, not with the creating a new tui terminal. ([`f1c734f`](https://github.com/byron/prodash/commit/f1c734f92150eff728444042b6c7cbac90989be9)) - Functionality for Alternate screens ([`dcc92ab`](https://github.com/byron/prodash/commit/dcc92ab05599800940359ac6c37d1e70e15c82f7)) - Initial terminal implementation, more generic, for tui crossterm ([`e7c07be`](https://github.com/byron/prodash/commit/e7c07be5d93feecc402d0c0793e4a3653c639651)) - Add everything required to build with stream support ([`2d60cc0`](https://github.com/byron/prodash/commit/2d60cc0149f80197b865312fab84575993ef5df0)) - Add input stream functions ([`21f4147`](https://github.com/byron/prodash/commit/21f41475e4c07539da49c370f37bd9c73611e94b)) - Refactor ([`42b8374`](https://github.com/byron/prodash/commit/42b83740c63a928ff3e067fef417d574b1186656)) - Add Key input transformation ([`bbf2651`](https://github.com/byron/prodash/commit/bbf2651e379b5758d53a889d9fb220c616d2a096)) - Initial version of crosstermium ([`25c8a98`](https://github.com/byron/prodash/commit/25c8a986ffb96e7c5783478c796c849a258c2ae0))
## v5.0.0 (2020-07-03) Support for windows by using Crossbeam by default. A first low-effort move to the latest version should be to set the dependency to `default-features = false, features = ["render-tui", "render-tui-termion", "localtime", "log-renderer"]` to get the same configuration as before. To try crossbeam, use `with-crossbeam` instead of `render-tui-termion`. If you have been using the event stream to send your own keys, swap `termion::event::Key` with `prodash::tui::input::Key`. ### Commit Statistics - 16 commits contributed to the release. - 2 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Major release: windows support ([`656cb6a`](https://github.com/byron/prodash/commit/656cb6a52625ce5c6bb0e35e84f077b1c2b2243b)) - Make with-crossbeam the default for maximum compatibility ([`9b8be9e`](https://github.com/byron/prodash/commit/9b8be9e41db9ecf8dd6509e081aa3d26863839f6)) - Fix makefile check; remove termion from dev dependenices ([`aa5827d`](https://github.com/byron/prodash/commit/aa5827d5988adf931f499b95df092713ab97a2b1)) - Make runtime into compile time error, nice! ([`7fc5961`](https://github.com/byron/prodash/commit/7fc5961d8a7059011f0c9774e5ec10a6240df672)) - Refactor; actually try compiling on windows ([`52f2592`](https://github.com/byron/prodash/commit/52f2592461d6289b944d4dee339eb60cec46e0c9)) - Refactor ([`72fe1bb`](https://github.com/byron/prodash/commit/72fe1bbd8dcbc79424f40f47e487fb1be5f9b8b6)) - Refactor ([`ababea5`](https://github.com/byron/prodash/commit/ababea51c0bddf82d634fc3d6e0e7d3448d074dc)) - Fix crossterm input handling ([`8547c8b`](https://github.com/byron/prodash/commit/8547c8bc4489c5934653757250b2735102ea5493)) - First crossterm input event loop, but looks like keys are not converted properly ([`9e74ada`](https://github.com/byron/prodash/commit/9e74ada3fd730ff90110bad813fffa4f1c86a683)) - Implement crossterm buffer creation (alternate + raw) ([`eaf904b`](https://github.com/byron/prodash/commit/eaf904bfb1014f94bc0422ef167c671bf0b1794c)) - Fix accidental import ([`659065d`](https://github.com/byron/prodash/commit/659065d7f44439104dff6c8128c0ed7a4b08aca5)) - And map crossterm modifiers on a best-effort basis ([`bafb189`](https://github.com/byron/prodash/commit/bafb189d3d200d57edcc9c7936e3ac37fb6640a0)) - First stab as crossterm key event mapping ([`6650a36`](https://github.com/byron/prodash/commit/6650a368df002c2548b926cc72852541d9f3fd46)) - Document all tui-renderer toggles and test termion feature toggles ([`27ccdc1`](https://github.com/byron/prodash/commit/27ccdc188d4431a88ea8be0248a9dcc755c9eaa8)) - Allow graceful failure if no backend is chosen. ([`1f36d96`](https://github.com/byron/prodash/commit/1f36d9644983cea6c465cdc290d6c1f8d3e3b2f0)) - Put all usage of termion behind a feature flag and unify Key input ([`3a1dc75`](https://github.com/byron/prodash/commit/3a1dc75a0c4ca3f5b888c68de91b420f261837dd))
## v4.1.0 (2020-07-01) Allow the TUI to automatically stop if there is no progress to display. This way, it's easier to use `prodash::tui` for visualizing finite tasks, which originally it wasn't intended for. Previously, in order to achieve the same, one would have to initialize the TUI with an event stream and send the Event for shutting down once the task at hand is complete. ### Commit Statistics - 2 commits contributed to the release. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Bump minor version ([`6755f32`](https://github.com/byron/prodash/commit/6755f326f852b1cf9a8ec64d31adc7bbb3fdcaa7)) - Allow the TUI to automatically stop if there is no progress to display ([`4fb6079`](https://github.com/byron/prodash/commit/4fb607994b72dcf17965b0424f495ef9d0875400))
## v4.0.5 (2020-07-01) Fix delayed reset of the terminal. Previously even after the future was dropped, it seemed like the terminal wasn't reset and the user was required to explicitly flush stdout to make the changes appear. This is due to the flushing previously happening too early, that is, before the `terminal` was dropped which emits the respective terminal escape codes at this time. Now the terminal instance is dropped explicitly right before emitting a flush. One might argue that the flush should happen in the terminal instance itself, but fixing that is out of scope. ### Commit Statistics - 1 commit contributed to the release. - 1 day passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Bump patch level ([`f8b06a6`](https://github.com/byron/prodash/commit/f8b06a6a298928caaf2a6e6e9b2e3135d0ae443b))
## v4.0.4 (2020-06-29) - Simplify `message()` trait bounds ### Commit Statistics - 2 commits contributed to the release. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Simplify traitbounds; bump patch level ([`be4240e`](https://github.com/byron/prodash/commit/be4240e94aacdde26b58d68ce7550c3e1aaa0094)) - (cargo-release) start next development iteration 4.0.4-alpha.0 ([`929225d`](https://github.com/byron/prodash/commit/929225dee1e2c8936a27d127765019ecbc9ee1ad))
## v4.0.3 (2020-06-29) - Remove piper in favor of futures-channel (which was included anyway) ### Commit Statistics - 4 commits contributed to the release over the course of 33 calendar days. - 42 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Remove async-channel in favor of (anyway imported) futures-channel ([`e4c2501`](https://github.com/byron/prodash/commit/e4c250106c2f1d81a88793c7ee74f74f21b35d68)) - Prepare next release ([`9879999`](https://github.com/byron/prodash/commit/9879999f66bccad3e4d43173bbaa5cc9a5126417)) - Update dependencies ([`73c25c2`](https://github.com/byron/prodash/commit/73c25c2ca1d95188543574ab1490961f69d001cf)) - Optimize include directive with 'cargo diet' ([`2978d2a`](https://github.com/byron/prodash/commit/2978d2a40b5d2a421029f7838515857cbfb45f08))
## v4.0.2 (2020-05-17) - Upgrade to latest TUI and TUI-react crates ### Commit Statistics - 1 commit contributed to the release. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Upgrade tui-* crates; bump patch level ([`7b796d1`](https://github.com/byron/prodash/commit/7b796d14cc5d2c9ceb3118e0cbfa3e3abfa86668))
## v4.0.1 (2020-05-17) - Reduce theoretical direct dependencies by not using 'futures' crate directly ### Commit Statistics - 2 commits contributed to the release. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Bump patch level ([`bf4fa4f`](https://github.com/byron/prodash/commit/bf4fa4f2fdea700f1ce254518d6791348dbe9a45)) - Reduce amount of dependencies by 5 ([`03970db`](https://github.com/byron/prodash/commit/03970db3d56a6ac960e95f6c67b8e4250be86791))
## v4.0.0 (2020-05-17) Switch from futures executor to smol. This actually simplifies some parts of the implementation, while fixing issues along futures not being dropped while they were on a thread pool. Now, for the example, no threadpool is used anymore. **Note** that this also means that in order for each frame to be drawn, one would have to invoke `smol::run` in one thread to activate the reactor which processes the timeout/ticker. Alternatively, one would send `Tick` events through a channel to trigger a redraw manually. ### Commit Statistics - 1 commit contributed to the release. - 12 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Replace futures-timer with smol; major version bump ([`ecb7dc8`](https://github.com/byron/prodash/commit/ecb7dc8e925c4acea08908c5c61e453d553ceec7))
## v3.6.3 (2020-05-05) - Fix out-of-bounds access (and panic) due to new and more precise progress bars ### Commit Statistics - 2 commits contributed to the release. - 1 day passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Bump patch level ([`2803e46`](https://github.com/byron/prodash/commit/2803e4698bb01fea2ff06a67af7b7db5e87da564)) - A precaution to make out-of-bounds access impossible ([`205a0ef`](https://github.com/byron/prodash/commit/205a0ef418fa5ec645191eecdf5adfe47f0051df))
## v3.6.2 (2020-05-03) - More horizontally precise progress bars; progress bars are now have lines between them vertically ### Commit Statistics - 5 commits contributed to the release over the course of 21 calendar days. - 24 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Bump patch level; fix crash due to integer underflow/overlow ([`af60c44`](https://github.com/byron/prodash/commit/af60c44ad27eab2cc0bdcda7b37b2df1c1176f48)) - More fine-grained progress bars, both horizontally and vertically ([`d6b7b3f`](https://github.com/byron/prodash/commit/d6b7b3f23215d281b8cecf64423036ddb3ae9081)) - Add clippy and fmt lints to actions ([`c56825c`](https://github.com/byron/prodash/commit/c56825cb46233289f8e638f3b7b941fcf09e8592)) - Bye bye travis, it was a good time! ([`d29fe5c`](https://github.com/byron/prodash/commit/d29fe5cbfaec8e492bda9c5a6821c88f5b227b41)) - Add github actions to compile and test ([`9efa70c`](https://github.com/byron/prodash/commit/9efa70c966446f9393b9495102ea892e4c3a989e))
## v3.6.1 (2020-04-09) - Properly respond to state changes even when 'redraw_only_on_state_change' is enabled ### Commit Statistics - 1 commit contributed to the release. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Patch bump; redraw also when the state changes, otherwise TUI feels laggy ([`93fce71`](https://github.com/byron/prodash/commit/93fce7193d4fc926ab0d31c868051c22b7258ece))
## v3.6.0 (2020-04-09) - A TUI option to only redraw if the progress actually changed. Useful if the change rate is lower than the frames per second. ### Commit Statistics - 2 commits contributed to the release. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Bump minor ([`f209f60`](https://github.com/byron/prodash/commit/f209f6034492422550d2de24fbcecdee71a370cf)) - Add capability to redraw only on state change ([`5fa836f`](https://github.com/byron/prodash/commit/5fa836fa8f035e73c0e4ee3174725ef28c2a93a0))
## v3.5.1 (2020-04-09) - Don't copy messages if the message pane is hidden, saving time ### Commit Statistics - 1 commit contributed to the release. - 5 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Bump patch level; copy messages only if the message pane is shown ([`353763b`](https://github.com/byron/prodash/commit/353763bfd588285e1e3770ea9b52a0a1fe179837))
## v3.5.0 (2020-04-03) - Cleaner visuals for hierarchical progress items, these won't show lines if there are no direct children with progress ### Commit Statistics - 3 commits contributed to the release. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Bump minor ([`39fbc16`](https://github.com/byron/prodash/commit/39fbc16a592703bd6d946a5ee5e2d1a48156042d)) - Don't show lines if these nodes are not actually children with progress ([`058b73a`](https://github.com/byron/prodash/commit/058b73a1f603e1538ba24172ce0715bfc0048b4a)) - Fix badge ([`97df7da`](https://github.com/byron/prodash/commit/97df7da752b09578aa7adad0d7c2d1866e6570f3))
## v3.4.1 (2020-04-02) - Enable localtime support by default ### Commit Statistics - 3 commits contributed to the release. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Bump minor - enable localtime by default ([`662f186`](https://github.com/byron/prodash/commit/662f186562423de7b955e24ba8432ea70ae6092b)) - Enable localtime by default - pulling time in isn't all that bad ([`699beba`](https://github.com/byron/prodash/commit/699bebaa9028602cd082f2b85f6531730db8e22b)) - Update asciinema video ([`0f07b68`](https://github.com/byron/prodash/commit/0f07b68ee60f1c69b3ea64f9d7792114fd5a293b))
## v3.4.0 (2020-04-02) - Even nicer tree rendering, along with screen space savings ### Commit Statistics - 24 commits contributed to the release over the course of 2 calendar days. - 2 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Bump minor ([`bbdc625`](https://github.com/byron/prodash/commit/bbdc6252130c05770b6524222435e951e2ded031)) - Better orphan display, with dots leading up to the node's level ([`4d69c34`](https://github.com/byron/prodash/commit/4d69c3436ae0e1d5345adeff7ca8cdf575961fa5)) - Use space much more efficiently in the tree view as well ([`3430a8d`](https://github.com/byron/prodash/commit/3430a8d46e251b166aba70dd078ce914de845dbf)) - Omit line between tree on left and progress bars to safe space ([`a3e5fe5`](https://github.com/byron/prodash/commit/a3e5fe5d2489028bc764fc0e771707419176a957)) - Oh my, that was sooooo worth it! ([`31bb906`](https://github.com/byron/prodash/commit/31bb906c643c67d95735b22d0aba0d883425036e)) - Looks pretty good already, now the tweaking ([`7f16472`](https://github.com/byron/prodash/commit/7f164723dca2723c9e37fd4773820576049f4f6d)) - Make orphan nodes work ([`497fbce`](https://github.com/byron/prodash/commit/497fbcee0d7ceda90eafbf3800d8ca68f6da0c3a)) - All tests green for the first time! ([`edf2024`](https://github.com/byron/prodash/commit/edf20248be59998553a47a70a764a64fb69b482c)) - Maybe a tiny step closer, definitely better/fixed index handling ([`42b31da`](https://github.com/byron/prodash/commit/42b31da9262c6842243a998bb98960b5786b9b1e)) - Before it all goes down the drain - only one thing to fix - let's checkpoint it ([`83a89ee`](https://github.com/byron/prodash/commit/83a89ee194db870bf267bf12f6ee73daaff1502d)) - I think the problem is that I try to squash a hiarchy level. Let's not do that ([`3c77eab`](https://github.com/byron/prodash/commit/3c77eab4152683babc1921022397ece103e1a790)) - Is this ever going to work? ([`8236d55`](https://github.com/byron/prodash/commit/8236d557eb03179e382107bc3cdb40cb1c488aba)) - A little closer, but needs way more tests :D ([`14876ee`](https://github.com/byron/prodash/commit/14876ee3fd5f236da9d8811096400f1728a31e3f)) - Refactor ([`8f6061f`](https://github.com/byron/prodash/commit/8f6061ffa75d18da2ac51d701700a90adc3b71b6)) - Better, but only works correctly for a single level - more nesting required Please enter the commit message for your changes. Lines starting ([`4083c4e`](https://github.com/byron/prodash/commit/4083c4e77fce5b12adb837438fb43ba7de98b642)) - Now we are actually closing in for this to work…OMG! ([`e9d268f`](https://github.com/byron/prodash/commit/e9d268f6ef670ec3c4428166d22e30c74d5eaab8)) - A little better ([`15a2b02`](https://github.com/byron/prodash/commit/15a2b02110135326598699733ba127a905aade2b)) - Add first tests for adjacency computation - it's not exactly working out of the box *:D ([`4e033b4`](https://github.com/byron/prodash/commit/4e033b4f20cca4d23ac23a3a42fd13ed50f332d5)) - Add everything needed to make use of the new system, and it's totally not working :D ([`40690b6`](https://github.com/byron/prodash/commit/40690b66f70ad96feddf92b6377838abeec76dbd)) - This might even work…one day :D ([`4652654`](https://github.com/byron/prodash/commit/46526549cb1392dc37552effa0837f146116d69c)) - Getting there, slowly, need refactor ([`46d015c`](https://github.com/byron/prodash/commit/46d015cbc3de26f54185f78fa0af8f90054f08bf)) - First sketch on getting an adjecency map by searching a sorted list of entries ([`0838165`](https://github.com/byron/prodash/commit/08381650f157f9430d7155d3543cb0bdfeab4864)) - Draw progress lines without ellipsis; make clearer that this happens ([`678fbaa`](https://github.com/byron/prodash/commit/678fbaa085ea0680f7f2212d27e6a577f65091b4)) - Improve tree drawing further ([`adc49c6`](https://github.com/byron/prodash/commit/adc49c624b21b6e200c4d00e29406c1262fd0c61))
## v3.3.0 (2020-03-31) - Much nicer task tree visualization ### Commit Statistics - 6 commits contributed to the release over the course of 1 calendar day. - 2 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Bump minor ([`cf9ddb8`](https://github.com/byron/prodash/commit/cf9ddb89414a844120b60b2a5d004a411f73cf49)) - Much nicer display of trees, now also on the level of progress bars ([`706d658`](https://github.com/byron/prodash/commit/706d6589b23b0df3f634017f53189074997d38f5)) - Refactor ([`536b4c8`](https://github.com/byron/prodash/commit/536b4c878206f9c6e6fcf443bae701af628cb0d4)) - A nicer way to draw the progress - show the tree even in the progress bars ([`840bb3a`](https://github.com/byron/prodash/commit/840bb3a58099b13086fe325631d8858f6546252b)) - Much better tree display, even though the code doing it is a bit wonky :D ([`833bb98`](https://github.com/byron/prodash/commit/833bb982eb76ead49e86f021537a66a2af404b4b)) - Use all functionality that now is available in tui-react ([`f5b1ee1`](https://github.com/byron/prodash/commit/f5b1ee109be8000bdae1dd036d82846ceaeb905d))
## v3.2.0 (2020-03-28) - Application can control if the GUI will respond to interrupt requests ### Commit Statistics - 2 commits contributed to the release. - 2 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Bump minor; dont' discard previous value of 'interrupt requested' ([`be1ce75`](https://github.com/byron/prodash/commit/be1ce7526ba6d8abf85371f6685664390835ba01)) - Visualize the interrupt-requested state, while providing useful information ([`273a1a6`](https://github.com/byron/prodash/commit/273a1a62bf29a6562c246079c3deeb182cdf4ebe))
## v3.1.1 (2020-03-25) - Bugfix (really): Finally delayed column resizing works correctly. ### Commit Statistics - 1 commit contributed to the release. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Bump patch - fix column sizing for good… ([`5029823`](https://github.com/byron/prodash/commit/50298236bfd32de329862540bdd00e1e1fae9fec))
## v3.1.0 (2020-03-25) - Tree::halted(…) indicates interruptable tasks without progress. Tree::blocked(…) means non-interruptable without progress. ### Commit Statistics - 1 commit contributed to the release. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Support for 'halted' state to indicate interruptible tasks; bump minor ([`b5ef271`](https://github.com/byron/prodash/commit/b5ef2716b52ecf145c0aff458a05f44b0792ce7a))
## v3.0.2 (2020-03-25) - Bugfix: Allow column-width computation to recover from becoming 0 ### Commit Statistics - 1 commit contributed to the release. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Allow column width computation to recover from 0; bump patch ([`856b7ee`](https://github.com/byron/prodash/commit/856b7ee3206d60cb9038f06822515fba0e23688d))
## v3.0.1 (2020-03-25) - Bugfix: Don't allow values of 0 for when to recompute task column widths ### Other - assure we never try to do 'x % 0' :D ### Commit Statistics - 1 commit contributed to the release. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Assure we never try to do 'x % 0' :D ([`82baf26`](https://github.com/byron/prodash/commit/82baf266045d44ba31aad4e570c687d7c51d0df7))
## v3.0.0 (2020-03-25) - New TUI option to delay computation of column width for stability with rapidly changing tasks ### Commit Statistics - 4 commits contributed to the release. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Bump version to 3.0.0 ([`bc46acc`](https://github.com/byron/prodash/commit/bc46accd27868ad3bbb21321bee0af114d185fce)) - Allow to control column width recomputation ([`d4dc966`](https://github.com/byron/prodash/commit/d4dc966dc5ae1d907f7b830a03477f84baead855)) - Support to compute column size only every so many ticks… ([`98e0f63`](https://github.com/byron/prodash/commit/98e0f630ffe858b989e7c973de1556a4477a0c8f)) - Example dashboard: Make sure ticker for context does not outpace FPS ([`c25217b`](https://github.com/byron/prodash/commit/c25217b54fd23d2c450211763a16270f39c3efdb))
## v2.1.0 (2020-03-24) - Optional cargo feature "localtime" shows all times in the local timezone ### Commit Statistics - 2 commits contributed to the release. - 1 day passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Add cargo feature 'localtime' to show all dates in localtime ([`a87f8cd`](https://github.com/byron/prodash/commit/a87f8cd0e9b9ea70a4a50b954ec3aa3160f2b3ab)) - Make chrono dependency available via re-exports ([`a5d4423`](https://github.com/byron/prodash/commit/a5d44233c56a9a7e05050e95c1aeaf591546bdcb))
## v2.0.1 (2020-03-23) - fix integer underflow with graphemes that report width of 0 ### Commit Statistics - 2 commits contributed to the release over the course of 1 calendar day. - 15 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Fix integer underflow with graphemes of width 0 ([`d9808cd`](https://github.com/byron/prodash/commit/d9808cd518b80fb4032a9176f1b1ec4dc5a5fdef)) - Transfer to new github location ([`6fa2e8d`](https://github.com/byron/prodash/commit/6fa2e8dc1b795c5614882e95e184810f7f1e466e))
## v2.0.0 (2020-03-07) * BREAKING: `progress.blocked(eta)` now takes a statically known reason for the blocked state `progress.blocked(reason, eta)`. This is useful to provide more context. ### Commit Statistics - 1 commit contributed to the release. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Reasons for block states; major version bump ([`74abaeb`](https://github.com/byron/prodash/commit/74abaeb4486a3a7b6889c3ed99244ab2c1b0bbf7))
## v1.2.0 (2020-03-07) * Support for eta messages in blocked unbounded tasks ### Commit Statistics - 1 commit contributed to the release. - 4 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - ETA messages in blocked unbounded tasks ([`d42c0d5`](https://github.com/byron/prodash/commit/d42c0d52f803595a48594a91b3278ce57ff8b95b))
## v1.1.6 (2020-03-02) * improve API symmetry by providing a `Tree::name()` to accompany `Tree::set_name(…)` ### Commit Statistics - 2 commits contributed to the release over the course of 5 calendar days. - 8 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Improve API symmetry ([`759f1f4`](https://github.com/byron/prodash/commit/759f1f4164f21804336f2c5677322b59c4218f7a)) - Put changelog into lib.rs, where it already was :) ([`dfc810b`](https://github.com/byron/prodash/commit/dfc810b7b012e536353ceeed61f2ca7ed935930d))
## v1.1.5 (2020-02-23) * Flush stdout when the TUI stopped running. That way, the alternate/original screen will be shown right away. ### Commit Statistics - 1 commit contributed to the release. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - V1.1.5 - flush stdout right away ([`8734668`](https://github.com/byron/prodash/commit/87346682d74951ed332a2533c00232095cc1d40b))
## v1.1.4 (2020-02-23) * Don't pretend to use &str if in fact an owned string is required. This caused unnecessary clones for those who pass owned strings. ### Commit Statistics - 3 commits contributed to the release. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Version bump ([`15d9681`](https://github.com/byron/prodash/commit/15d9681acd734c8518a541f621ccc69c24d9a0f3)) - Cargo clippy ([`3adba17`](https://github.com/byron/prodash/commit/3adba17c71b8e98634cf651219ce247b2182174e)) - Don't pretend we only need str ([`d05158b`](https://github.com/byron/prodash/commit/d05158b135bab195dc8a34a573cee120079146b2))
## v1.1.3 (2020-02-23) * hide cursor or a nicer visual experience ### Commit Statistics - 3 commits contributed to the release. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Hide cursor, bump version ([`ecf5cc4`](https://github.com/byron/prodash/commit/ecf5cc4a55536d36290b8bea1f816d3d29bb5468)) - Run the few doc tests we have ([`9b28cc0`](https://github.com/byron/prodash/commit/9b28cc0fce171bbadf736d25d8aec1614f24caf7)) - (cargo-release) start next development iteration 1.1.3-alpha.0 ([`cae0e02`](https://github.com/byron/prodash/commit/cae0e024d017d84962dc459e200d361c372f9c89))
## v1.1.2 (2020-02-22) ### Commit Statistics - 5 commits contributed to the release. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages ### Commit Details
view details * **Uncategorized** - Show travis badge ([`65b48f1`](https://github.com/byron/prodash/commit/65b48f1270310453e7b03363ddfdad5551aa6900)) - Bump version for new crate meta-data ([`c557b6b`](https://github.com/byron/prodash/commit/c557b6b05206822e2b01feb079d2b8745b408019)) - Travis support ([`507cc20`](https://github.com/byron/prodash/commit/507cc207742b536a0bd92a4357f6cfc1b5b2126b)) - Adjust repository link ([`d75d91f`](https://github.com/byron/prodash/commit/d75d91fe16e32747bb6400cbe3b3e63c4399f123)) - Initial commit, as copied from cli ([`708901b`](https://github.com/byron/prodash/commit/708901b796110bd49bcac6bc1ef1daa7b1931720))
## v1.1.0 * fix toggles - previously prodash, withoug tui, would always build humantime and unicode width * add support for logging as user interface ## v0.7.0 (2021-05-02) ## v0.6.0 (2021-01-04) ## v0.5.0 (2020-11-15) ## v0.4.0 (2020-09-28) ## v0.3.2 (2020-09-14) ## v0.3.1 (2020-09-13) ## v0.3.0 (2020-07-22) ## v0.2.0 (2020-07-11) ## v0.1.4 (2020-07-06) ## v0.1.3 (2020-07-06) ## v0.1.2 (2020-07-06) ## v0.1.1 (2020-07-05) prodash-28.0.0/Cargo.lock0000644000001164270000000000100105510ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "ahash" version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" dependencies = [ "cfg-if", "once_cell", "version_check", "zerocopy", ] [[package]] name = "allocator-api2" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" [[package]] name = "anes" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "ansiterm" version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ab587f5395da16dd2e6939adf53dede583221b320cadfb94e02b5b7b9bf24cc" dependencies = [ "winapi", ] [[package]] name = "anstyle" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" [[package]] name = "argh" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab257697eb9496bf75526f0217b5ed64636a9cfafa78b8365c71bd283fcef93e" dependencies = [ "argh_derive", "argh_shared", ] [[package]] name = "argh_derive" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b382dbd3288e053331f03399e1db106c9fb0d8562ad62cb04859ae926f324fa6" dependencies = [ "argh_shared", "proc-macro2", "quote", "syn 1.0.109", ] [[package]] name = "argh_shared" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64cb94155d965e3d37ffbbe7cc5b82c3dd79dd33bd48e536f73d2cfb8d85506f" [[package]] name = "async-channel" version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf46fee83e5ccffc220104713af3292ff9bc7c64c7de289f66dae8e38d826833" dependencies = [ "concurrent-queue", "event-listener 2.5.3", "futures-core", ] [[package]] name = "async-channel" version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ca33f4bc4ed1babef42cad36cc1f51fa88be00420404e5b1e80ab1b18f7678c" dependencies = [ "concurrent-queue", "event-listener 4.0.0", "event-listener-strategy", "futures-core", "pin-project-lite", ] [[package]] name = "async-executor" version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fa3dc5f2a8564f07759c008b9109dc0d39de92a88d5588b8a5036d286383afb" dependencies = [ "async-lock 2.7.0", "async-task", "concurrent-queue", "fastrand 1.9.0", "futures-lite 1.13.0", "slab", ] [[package]] name = "async-io" version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6d3b15875ba253d1110c740755e246537483f152fa334f91abd7fe84c88b3ff" dependencies = [ "async-lock 3.2.0", "cfg-if", "concurrent-queue", "futures-io", "futures-lite 2.1.0", "parking", "polling", "rustix", "slab", "tracing", "windows-sys 0.52.0", ] [[package]] name = "async-lock" version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa24f727524730b077666307f2734b4a1a1c57acb79193127dcc8914d5242dd7" dependencies = [ "event-listener 2.5.3", ] [[package]] name = "async-lock" version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7125e42787d53db9dd54261812ef17e937c95a51e4d291373b670342fa44310c" dependencies = [ "event-listener 4.0.0", "event-listener-strategy", "pin-project-lite", ] [[package]] name = "async-task" version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ecc7ab41815b3c653ccd2978ec3255c81349336702dfdf62ee6f7069b12a3aae" [[package]] name = "atomic-waker" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3" [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" [[package]] name = "blocking" version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77231a1c8f801696fc0123ec6150ce92cffb8e164a02afb9c8ddee0e9b65ad65" dependencies = [ "async-channel 1.8.0", "async-lock 2.7.0", "async-task", "atomic-waker", "fastrand 1.9.0", "futures-lite 1.13.0", "log", ] [[package]] name = "bytesize" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38fcc2979eff34a4b84e1cf9a1e3da42a7d44b3b690a40cdcb23e3d556cfb2e5" [[package]] name = "cassowary" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" [[package]] name = "cast" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[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 = "ciborium" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926" dependencies = [ "ciborium-io", "ciborium-ll", "serde", ] [[package]] name = "ciborium-io" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656" [[package]] name = "ciborium-ll" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b" dependencies = [ "ciborium-io", "half", ] [[package]] name = "clap" version = "4.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3eab9e8ceb9afdade1ab3f0fd8dbce5b1b2f468ad653baf10e771781b2b67b73" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" version = "4.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f2763db829349bf00cfc06251268865ed4363b93a943174f638daf3ecdba2cd" dependencies = [ "anstyle", "clap_lex", ] [[package]] name = "clap_lex" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" [[package]] name = "concurrent-queue" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c" dependencies = [ "crossbeam-utils", ] [[package]] name = "criterion" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" dependencies = [ "anes", "cast", "ciborium", "clap", "criterion-plot", "is-terminal", "itertools 0.10.5", "num-traits", "once_cell", "oorandom", "regex", "serde", "serde_derive", "serde_json", "tinytemplate", "walkdir", ] [[package]] name = "criterion-plot" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" dependencies = [ "cast", "itertools 0.10.5", ] [[package]] name = "crossbeam-utils" version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" dependencies = [ "cfg-if", ] [[package]] name = "crossterm" version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" dependencies = [ "bitflags 2.3.3", "crossterm_winapi", "futures-core", "libc", "mio", "parking_lot", "signal-hook", "signal-hook-mio", "winapi", ] [[package]] name = "crossterm_winapi" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" dependencies = [ "winapi", ] [[package]] name = "crosstermion" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "400415b86f4dc01b9e4e129e822dad900e546287319da7ab229654978d3e07e1" dependencies = [ "ansiterm", "async-channel 2.1.1", "crossterm", "futures-channel", "futures-core", "futures-lite 2.1.0", "ratatui", "tui-react", ] [[package]] name = "ctrlc" version = "3.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbcf33c2a618cbe41ee43ae6e9f2e48368cd9f9db2896f10167d8d762679f639" dependencies = [ "nix", "windows-sys 0.45.0", ] [[package]] name = "dashmap" version = "5.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc" dependencies = [ "cfg-if", "hashbrown 0.12.3", "lock_api", "once_cell", "parking_lot_core", ] [[package]] name = "either" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "env_logger" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" dependencies = [ "humantime", "log", ] [[package]] name = "errno" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" 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 = "event-listener" version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "event-listener" version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "770d968249b5d99410d61f5bf89057f3199a077a04d087092f58e7d10692baae" dependencies = [ "concurrent-queue", "parking", "pin-project-lite", ] [[package]] name = "event-listener-strategy" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" dependencies = [ "event-listener 4.0.0", "pin-project-lite", ] [[package]] name = "fastrand" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" dependencies = [ "instant", ] [[package]] name = "fastrand" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" [[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-lite" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" dependencies = [ "fastrand 1.9.0", "futures-core", "futures-io", "memchr", "parking", "pin-project-lite", "waker-fn", ] [[package]] name = "futures-lite" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aeee267a1883f7ebef3700f262d2d54de95dfaf38189015a74fdc4e0c7ad8143" dependencies = [ "fastrand 2.0.1", "futures-core", "futures-io", "parking", "pin-project-lite", ] [[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 2.0.39", ] [[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.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" dependencies = [ "cfg-if", "libc", "wasi", ] [[package]] name = "half" version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" [[package]] name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" dependencies = [ "ahash", "allocator-api2", ] [[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.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" [[package]] name = "human_format" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86cce260d758a9aa3d7c4b99d55c815a540f8a37514ba6046ab6be402a157cb0" [[package]] name = "humantime" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "indoc" version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e186cfbae8084e513daff4240b4797e342f988cecda4fb6c939150f96315fd8" [[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 = "itertools" version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ "either", ] [[package]] name = "itertools" version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0" dependencies = [ "either", ] [[package]] name = "itoa" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" [[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.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" [[package]] name = "lock_api" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" dependencies = [ "autocfg", "scopeguard", ] [[package]] name = "log" version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ "cfg-if", ] [[package]] name = "lru" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2994eeba8ed550fd9b47a0b38f0242bc3344e496483c6180b69139cc2fa5d1d7" dependencies = [ "hashbrown 0.14.3", ] [[package]] name = "memchr" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "mio" version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" dependencies = [ "libc", "log", "wasi", "windows-sys 0.45.0", ] [[package]] name = "nix" version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" dependencies = [ "bitflags 1.3.2", "cfg-if", "libc", "static_assertions", ] [[package]] name = "num-traits" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" dependencies = [ "autocfg", ] [[package]] name = "num_threads" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" dependencies = [ "libc", ] [[package]] name = "once_cell" version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] name = "oorandom" version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] name = "parking" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" [[package]] name = "parking_lot" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", "parking_lot_core", ] [[package]] name = "parking_lot_core" version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", "windows-sys 0.45.0", ] [[package]] name = "paste" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] name = "pin-project-lite" version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "polling" version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf63fa624ab313c11656b4cda960bfc46c410187ad493c41f6ba2d8c1e991c9e" dependencies = [ "cfg-if", "concurrent-queue", "pin-project-lite", "rustix", "tracing", "windows-sys 0.52.0", ] [[package]] name = "ppv-lite86" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" dependencies = [ "unicode-ident", ] [[package]] name = "prodash" version = "28.0.0" dependencies = [ "argh", "async-executor", "async-io", "blocking", "bytesize", "criterion", "crosstermion", "ctrlc", "dashmap", "env_logger", "futures", "futures-core", "futures-lite 2.1.0", "futures-util", "human_format", "humantime", "is-terminal", "log", "once_cell", "parking_lot", "rand", "ratatui", "signal-hook", "time", "tui-react", "unicode-segmentation", "unicode-width", ] [[package]] name = "quote" version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" 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 = "ratatui" version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5659e52e4ba6e07b2dad9f1158f578ef84a73762625ddb51536019f34d180eb" dependencies = [ "bitflags 2.3.3", "cassowary", "crossterm", "indoc", "itertools 0.12.0", "lru", "paste", "stability", "strum", "unicode-segmentation", "unicode-width", ] [[package]] name = "redox_syscall" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ "bitflags 1.3.2", ] [[package]] name = "regex" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" dependencies = [ "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" [[package]] name = "rustix" version = "0.38.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f" dependencies = [ "bitflags 2.3.3", "errno", "libc", "linux-raw-sys", "windows-sys 0.48.0", ] [[package]] name = "rustversion" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" [[package]] name = "ryu" version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" [[package]] name = "same-file" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" dependencies = [ "winapi-util", ] [[package]] name = "scopeguard" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "serde" version = "1.0.163" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.163" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" dependencies = [ "proc-macro2", "quote", "syn 2.0.39", ] [[package]] name = "serde_json" version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" dependencies = [ "itoa", "ryu", "serde", ] [[package]] name = "signal-hook" version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" dependencies = [ "libc", "signal-hook-registry", ] [[package]] name = "signal-hook-mio" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" dependencies = [ "libc", "mio", "signal-hook", ] [[package]] name = "signal-hook-registry" version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" dependencies = [ "libc", ] [[package]] name = "slab" version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" dependencies = [ "autocfg", ] [[package]] name = "smallvec" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "stability" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebd1b177894da2a2d9120208c3386066af06a488255caabc5de8ddca22dbc3ce" dependencies = [ "quote", "syn 1.0.109", ] [[package]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "strum" version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" dependencies = [ "strum_macros", ] [[package]] name = "strum_macros" version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" dependencies = [ "heck", "proc-macro2", "quote", "rustversion", "syn 2.0.39", ] [[package]] name = "syn" version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "syn" version = "2.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "time" version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f3403384eaacbca9923fa06940178ac13e4edb725486d70e8e15881d0c836cc" dependencies = [ "itoa", "libc", "num_threads", "serde", "time-core", "time-macros", ] [[package]] name = "time-core" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" [[package]] name = "time-macros" version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b" dependencies = [ "time-core", ] [[package]] name = "tinytemplate" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" dependencies = [ "serde", "serde_json", ] [[package]] name = "tracing" version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ "pin-project-lite", "tracing-core", ] [[package]] name = "tracing-core" version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" [[package]] name = "tui-react" version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ffacd73e2d4666c1aec4e85ab986354909e276f1dbff5c75f86bcebce566511" dependencies = [ "log", "ratatui", "unicode-segmentation", "unicode-width", ] [[package]] name = "unicode-ident" version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" [[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 = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "waker-fn" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" [[package]] name = "walkdir" version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" dependencies = [ "same-file", "winapi-util", ] [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ "winapi", ] [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" version = "0.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.0", ] [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets 0.52.0", ] [[package]] name = "windows-targets" version = "0.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.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" 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-targets" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" dependencies = [ "windows_aarch64_gnullvm 0.52.0", "windows_aarch64_msvc 0.52.0", "windows_i686_gnu 0.52.0", "windows_i686_msvc 0.52.0", "windows_x86_64_gnu 0.52.0", "windows_x86_64_gnullvm 0.52.0", "windows_x86_64_msvc 0.52.0", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.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_gnullvm" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" [[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_aarch64_msvc" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" [[package]] name = "windows_i686_gnu" version = "0.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_gnu" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" [[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_i686_msvc" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" [[package]] name = "windows_x86_64_gnu" version = "0.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_gnu" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" [[package]] name = "windows_x86_64_gnullvm" version = "0.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_gnullvm" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" [[package]] name = "windows_x86_64_msvc" version = "0.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 = "windows_x86_64_msvc" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "zerocopy" version = "0.7.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d075cf85bbb114e933343e087b92f2146bac0d55b534cbb8188becf0039948e" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" version = "0.7.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86cd5ca076997b97ef09d3ad65efe811fa68c9e874cb636ccb211223a813b0c2" dependencies = [ "proc-macro2", "quote", "syn 2.0.39", ] prodash-28.0.0/Cargo.toml0000644000000103310000000000100105570ustar # 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" name = "prodash" version = "28.0.0" authors = ["Sebastian Thiel "] include = [ "src/**/*", "README.md", "LICENSE.md", "CHANGELOG.md", ] description = "A dashboard for visualizing progress of asynchronous and possibly blocking tasks" readme = "README.md" license = "MIT" repository = "https://github.com/Byron/prodash" [package.metadata.docs.rs] all-features = true [lib] doctest = true [[example]] name = "dashboard" path = "examples/dashboard.rs" required-features = [ "render-tui", "render-tui-crossterm", "render-line", "render-line-crossterm", "signal-hook", "render-line-autoconfigure", "progress-tree", ] [[example]] name = "units" path = "examples/units.rs" required-features = [ "unit-bytes", "unit-duration", "unit-human", "render-tui", "render-tui-crossterm", "render-line", "render-line-crossterm", "signal-hook", ] [[bench]] name = "usage" path = "benches/usage.rs" harness = false [dependencies.async-io] version = "2.2.1" optional = true [dependencies.bytesize] version = "1.0.1" optional = true [dependencies.crosstermion] version = "0.13.0" optional = true default-features = false [dependencies.ctrlc] version = "3.1.4" features = ["termination"] optional = true default-features = false [dependencies.dashmap] version = "5.1.0" optional = true default-features = false [dependencies.futures-core] version = "0.3.4" optional = true default-features = false [dependencies.futures-lite] version = "2.1.0" optional = true [dependencies.human_format] version = "1.0.3" optional = true [dependencies.humantime] version = "2.1.0" optional = true [dependencies.is-terminal] version = "0.4.9" optional = true [dependencies.log] version = "0.4.8" optional = true [dependencies.parking_lot] version = "0.12.1" optional = true default-features = false [dependencies.signal-hook] version = "0.3.9" optional = true default-features = false [dependencies.time] version = "0.3.2" features = [ "std", "local-offset", "formatting", ] optional = true default-features = false [dependencies.tui] version = "0.25.0" optional = true default-features = false package = "ratatui" [dependencies.tui-react] version = "0.22.0" optional = true [dependencies.unicode-segmentation] version = "1.6.0" optional = true [dependencies.unicode-width] version = "0.1.7" optional = true [dev-dependencies.argh] version = "0.1.3" [dev-dependencies.async-executor] version = "1.1.0" [dev-dependencies.async-io] version = "2.2.1" [dev-dependencies.blocking] version = "1.0.0" [dev-dependencies.criterion] version = "0.5.1" default-features = false [dev-dependencies.env_logger] version = "0.10.0" features = ["humantime"] default-features = false [dev-dependencies.futures] version = "0.3.5" [dev-dependencies.futures-util] version = "0.3.4" default-features = false [dev-dependencies.is-terminal] version = "0.4.9" [dev-dependencies.once_cell] version = "1.4.0" [dev-dependencies.rand] version = "0.8.1" [features] default = [ "progress-tree", "progress-tree-log", ] local-time = ["time"] progress-log = ["log"] progress-tree = ["parking_lot"] progress-tree-hp-hashmap = ["dashmap"] progress-tree-log = ["log"] render-line = [ "crosstermion/color", "humantime", "unicode-width", ] render-line-autoconfigure = ["is-terminal"] render-line-crossterm = ["crosstermion/crossterm"] render-tui = [ "tui", "unicode-segmentation", "unicode-width", "crosstermion/input-async", "tui-react", "futures-lite", "futures-core", "async-io", "humantime", ] render-tui-crossterm = [ "crosstermion/tui-react-crossterm", "crosstermion/input-async-crossterm", ] unit-bytes = ["bytesize"] unit-duration = ["humantime"] unit-human = ["human_format"] prodash-28.0.0/Cargo.toml.orig000064400000000000000000000067351046102023000142550ustar 00000000000000[package] name = "prodash" version = "28.0.0" authors = ["Sebastian Thiel "] description = "A dashboard for visualizing progress of asynchronous and possibly blocking tasks" edition = "2021" include = ["src/**/*", "README.md", "LICENSE.md", "CHANGELOG.md"] license = "MIT" repository = "https://github.com/Byron/prodash" readme = "README.md" [lib] doctest = true [[example]] name = "dashboard" path = "examples/dashboard.rs" required-features = ["render-tui", "render-tui-crossterm", "render-line", "render-line-crossterm", "signal-hook", "render-line-autoconfigure", "progress-tree"] [[example]] name = "units" path = "examples/units.rs" required-features = [ "unit-bytes", "unit-duration", "unit-human", "render-tui", "render-tui-crossterm", "render-line", "render-line-crossterm", "signal-hook" ] [features] default = ["progress-tree", "progress-tree-log"] progress-tree = ["parking_lot"] progress-tree-hp-hashmap = ["dashmap"] progress-tree-log = ["log"] progress-log = ["log"] unit-bytes = ["bytesize"] unit-human = ["human_format"] unit-duration = ["humantime"] render-tui-crossterm = ["crosstermion/tui-react-crossterm", "crosstermion/input-async-crossterm"] render-tui = ["tui", "unicode-segmentation", "unicode-width", "crosstermion/input-async", "tui-react", "futures-lite", "futures-core", "async-io", "humantime"] render-line = ["crosstermion/color", "humantime", "unicode-width"] render-line-crossterm = ["crosstermion/crossterm"] render-line-autoconfigure = ["is-terminal"] local-time = ["time"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] dashmap = { version = "5.1.0", optional = true, default-features = false } parking_lot = { version = "0.12.1", optional = true, default-features = false } # with-logging log = { version = "0.4.8", optional = true } # render-tui tui = { package = "ratatui", version = "0.25.0", optional = true, default-features = false } tui-react = { version = "0.22.0", optional = true } futures-core = { version = "0.3.4", optional = true, default-features = false } futures-lite = { version = "2.1.0", optional = true } humantime = { version = "2.1.0", optional = true } unicode-segmentation = { version = "1.6.0", optional = true } unicode-width = { version = "0.1.7", optional = true } crosstermion = { version = "0.13.0", optional = true, default-features = false } async-io = { version = "2.2.1", optional = true } # localtime support for render-tui time = { version = "0.3.2", optional = true, features = ["std", "local-offset", "formatting"], default-features = false } # line renderer ctrlc = { version = "3.1.4", optional = true, default-features = false, features = ['termination'] } signal-hook = { version = "0.3.9", optional = true, default-features = false } is-terminal = { version = "0.4.9", optional = true } # units bytesize = { version = "1.0.1", optional = true } human_format = { version = "1.0.3", optional = true } [package.metadata.docs.rs] all-features = true [dev-dependencies] rand = "0.8.1" env_logger = { version = "0.10.0", default-features = false, features = ["humantime"] } criterion = { version = "0.5.1", default-features = false } futures-util = { version = "0.3.4", default-features = false } argh = "0.1.3" futures = "0.3.5" is-terminal = "0.4.9" blocking = "1.0.0" once_cell = "1.4.0" async-executor = "1.1.0" async-io = "2.2.1" [[bench]] name = "usage" path = "benches/usage.rs" harness = false prodash-28.0.0/LICENSE.md000064400000000000000000000021331046102023000127560ustar 00000000000000The MIT License (MIT) ===================== Copyright © `2020` `Sebastian Thiel` 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. prodash-28.0.0/README.md000064400000000000000000000167621046102023000126460ustar 00000000000000![Rust](https://github.com/Byron/prodash/workflows/Rust/badge.svg) [![Crates.io](https://img.shields.io/crates/v/prodash.svg)](https://crates.io/crates/prodash) **prodash** allows to integrate progress reporting into concurrent applications and provides renderers for displaying it in various ways. It's easy to integrate thanks to a pragmatic API, and comes with a terminal user interface by default. [![asciicast](https://asciinema.org/a/315956.svg)](https://asciinema.org/a/315956) [![asciicast](https://asciinema.org/a/346619.svg)](https://asciinema.org/a/346619) ## How to use… Be sure to read the documentation at https://docs.rs/prodash, it contains various examples on how to get started. Or run the demo application like so `cd prodash && cargo run --all-features --example dashboard`. ## Feature Toggles This crate comes with various cargo features to tailor it to your needs. * **progress-tree** _(default)_ * Provide a `Progress` and `Root` trait implementation for use with the `render-line` and `render-tui` backed by `dashmap`. * **progress-tree-hp-hashmap** - high-performance registry for pregree tree nodes in case of ultra-heavy insertions and deletions. * If this is necessary, it's probably impossible to resonably visualize the progress tree anyway, but the option exists nonetheless in case it is ever needed. Historically, this was the default, but now it seems simpler is better and just fine for typical programs. * **progress-tree-log** _(default)_ * If logging in the `log` crate is initialized, a `log` will be used to output all messages provided to `tree::Item::message(…)` and friends. No actual progress is written. * May interfere with `render-tui` or `render-line`, or any renderer outputting to the console. * **progress-log** * A `Progress` implementation which logs messages and progress using the `log` crate * **local-time** * If set, timestamps in the message pane of the `render-tui` will be using the local time, not UTC * If set, timestamps of the log messages of the `render-line` will be using the local time, not UTC * Has no effect without the `render-tui` or `render-line` respectively * **On Unix** one needs to provide flags to rustc when building the binary to acknowledge potential unsoundness: `RUSTFLAGS="--cfg unsound_local_offset" cargo build` will do the job, but there are [other ways](https://doc.rust-lang.org/cargo/reference/config.html#hierarchical-structure) to do that as well. * **render-line** * Provide a minimal line-based progress renderer which can be limited to a subset of the progress hierarchy. * It's like the render-tui, but with far less dependencies and less visual fidelity - all it needs is to move the cursor a little while drawing characters and block graphics. * Support for [clicolors spec](https://bixense.com/clicolors/) and [no-color spec](https://no-color.org) * Supports initial delay that won't affect log messages, showing progress only when needed, automatically. * Requires one of these additional feature flags to be set to be functional * **one required** _(mutually exclusive)_ * **render-line-crossterm** - use the _crossterm_ backend, useful for working on windows * **render-line-termion** - use the _termion_ backend, useful for lean unix-only builds * _Optional features_ * **render-line-autoconfigure** * If enabled, calls to `render::line::Options::auto_configure()` will configure the display based on whether or not we are in a terminal and set its color mode based on what's possible or desired. * **signal-hook** * If set, and the `hide_cursor` line renderer option is set, the cursor will be hidden **and** *SIG_INT* and *SIG_TERM* handlers will be installed to reset the cursor on exit. Otherwise you have to make sure to call `shutdown_and_wait()` on the `JoinHandle` returned to give the renderer a chance to undo the terminal changes. Failing to do so will leave the cusor hidden once the program has already finished. * Comes at the cost of an extra thread and additional dependencies. * **render-tui** * Provide a terminal user interface visualizing every detail of the current progress state. It treats the terminal as a matrix display. * Requires one of these additional feature flags to be set to be functional ** _(one required, mutually exclusive)_ * **render-tui-crossterm** * Use the `crossterm` crate as terminal backend * Works everywhere natively, but has more dependencies * You can set additional features like this `cargo build --features render-tui-crossterm,crossterm/event-stream` * **render-tui-termion** * Use the `termion` crate as terminal backend * It has less dependencies but works only on `unix` systems * to get this, disable default features and chose at least `render-tui` and `render-tui-termion`. * **unit-bytes** * Supports dynamic byte display using the tiny `bytesize` crate. * **unit-human** * Display counts in a way that is easier to grasp for humans, using the tiny `human_format` crate. * **unit-duration** * Displays time in seconds like '_5m4s_' using the tiny `humantime` crate. ## Features * fast insertions and updates for transparent progress tracking of highly concurrent programs * a messages buffer for information about success and failure * a terminal user interface for visualization, with keyboard controls and dynamic re-sizing * unicode and multi-width character support ## Limitations * the *line renderer* is inherently limited in the amount of progress it can display without visual artifacts. * it does copy quite some state each time it displays progress information and messages * The underlying sync data structure, `dashmap`, does not document every use of unsafe * I also evaluated `evmap`, which has 25% less uses of unsafe, but a more complex interface. * Thus far it seemed 'ok' to use, who knows… we are getting mutable pieces of a hashmap from multiple threads, however, we never hand out multiple handles to the same child which should make actual concurrent access to the same key impossible. * If there are more than 2^16 tasks * then * running concurrently on a single level of the tree, they start overwriting each other * over its lifetime, even though they do not run concurrently, eventually new tasks will seem like old tasks (their ID wrapped around) * why * on drop, they never decrement a child count used to generate a new ID * fix * make the id bigger, like u32 * we should do that once there is a performance test * If the log lines are too long for the terminal width when using the *line renderer* * then * visual artifacts will appear * why * trying to draw beyond the terminal boundary will add a line break automatically, which can cause unexpected overdraw. * **fix** * count amount of blocks drawn, without ansi codes, and stop drawing at the boundary. ## Lessons Learned * `drop()` is not garantueed to be called when the future returns Ready and is in the futures::executor::ThreadPool * Workaround: drop and cleanup explicitly, prone to forgetting it. * This is also why `futures::future::abortable()` works (by stopping the polling), but doesn't as cleanup is not performed, even though it clearly would be preferred. * fix * Use a join handle and await it - this will drop the future properly * `select()` might not work with complex futures - these should then be `boxed()` if `Unpin` isn't implemented. prodash-28.0.0/src/lib.rs000064400000000000000000000051151046102023000132600ustar 00000000000000#![deny(unsafe_code, missing_docs)] /*! Prodash is a dashboard for displaying the progress of concurrent application. It consists of two parts * a `Tree` to gather progress information and messages * a terminal user interface which displays this information, along with optional free-form information provided by the application itself Even though the `Tree` is not async, it's meant to be transparent and non-blocking performance wise, and benchmarks seem to indicate this is indeed the case. The **terminal user interface** seems to be the least transparent part, but can be configured to refresh less frequently. # Terminal User Interface By default, a TUI is provided to visualize all state. Have a look at [the example provided in the tui module](./tui/index.html). **Please note** that it is behind the `render-tui` feature toggle, which is enabled by default. # Logging If the feature `progress-tree-log` is enabled (default), most calls to `progress` will also be logged. That way, even without a terminal user interface, there will be progress messages. Please note that logging to stdout should not be performed with this feature enabled and a terminal user interface is shown, as this will seriously interfere with the TUI. # A demo application Please have a look at the [dashboard demo](https://github.com/Byron/crates-io-cli-rs/blob/master/prodash/examples/dashboard.rs). [![asciicast](https://asciinema.org/a/301838.svg)](https://asciinema.org/a/301838) Run it with `cargo run --example dashboard` and see what else it can do by checking out `cargo run --example dashboard -- --help`. */ #[cfg(feature = "atty")] pub use atty; #[cfg(feature = "progress-tree")] /// pub mod tree; /// pub mod render; #[cfg(feature = "progress-tree-log")] pub use log::info; #[cfg(feature = "progress-tree-log")] pub use log::warn; #[cfg(any(feature = "humantime", feature = "time"))] /// pub mod time; /// pub mod unit; #[doc(inline)] pub use unit::Unit; /// pub mod messages; /// pub mod progress; mod traits; pub use traits::{ BoxedDynNestedProgress, BoxedProgress, Count, DynNestedProgress, DynNestedProgressToNestedProgress, NestedProgress, Progress, Root, WeakRoot, }; mod throughput; pub use crate::throughput::Throughput; #[cfg(not(feature = "progress-tree-log"))] mod log { /// Stub #[macro_export(local_inner_macros)] macro_rules! warn { (target: $target:expr, $($arg:tt)+) => {}; ($($arg:tt)+) => {}; } /// Stub #[macro_export(local_inner_macros)] macro_rules! info { (target: $target:expr, $($arg:tt)+) => {}; ($($arg:tt)+) => {}; } } prodash-28.0.0/src/messages.rs000064400000000000000000000107511046102023000143230ustar 00000000000000use std::time::SystemTime; /// The severity of a message #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] pub enum MessageLevel { /// Rarely sent information related to the progress, not to be confused with the progress itself Info, /// Used to indicate that a task has failed, along with the reason Failure, /// Indicates a task was completed successfully Success, } /// A message to be stored along with the progress tree. /// /// It is created by [`Tree::message(…)`](./struct.Item.html#method.message). #[derive(Debug, Clone, Eq, PartialEq)] pub struct Message { /// The time at which the message was sent. pub time: SystemTime, /// The severity of the message pub level: MessageLevel, /// The name of the task that created the `Message` pub origin: String, /// The message itself pub message: String, } /// A ring buffer for messages. #[derive(Debug, Clone, Eq, PartialEq)] pub struct MessageRingBuffer { pub(crate) buf: Vec, cursor: usize, total: usize, } impl MessageRingBuffer { /// Create a new instance the ability to hold `capacity` amount of messages. pub fn with_capacity(capacity: usize) -> MessageRingBuffer { MessageRingBuffer { buf: Vec::with_capacity(capacity), cursor: 0, total: 0, } } /// Push a `message` from `origin` at severity `level` into the buffer, possibly overwriting the last message added. pub fn push_overwrite(&mut self, level: MessageLevel, origin: String, message: impl Into) { let msg = Message { time: SystemTime::now(), level, origin, message: message.into(), }; if self.has_capacity() { self.buf.push(msg) } else { self.buf[self.cursor] = msg; self.cursor = (self.cursor + 1) % self.buf.len(); } self.total = self.total.wrapping_add(1); } /// Copy all messages currently contained in the buffer to `out`. pub fn copy_all(&self, out: &mut Vec) { out.clear(); if self.buf.is_empty() { return; } out.extend_from_slice(&self.buf[self.cursor % self.buf.len()..]); if self.cursor != self.buf.len() { out.extend_from_slice(&self.buf[..self.cursor]); } } /// Copy all new messages into `out` that where received since the last time this method was called provided /// its `previous` return value. pub fn copy_new(&self, out: &mut Vec, previous: Option) -> MessageCopyState { out.clear(); match previous { Some(MessageCopyState { cursor, buf_len, total }) => { if self.total.saturating_sub(total) >= self.buf.capacity() { self.copy_all(out); } else { let new_elements_below_cap = self.buf.len().saturating_sub(buf_len); let cursor_ofs: isize = self.cursor as isize - cursor as isize; match cursor_ofs { // there was some capacity left without wrapping around 0 => { out.extend_from_slice(&self.buf[self.buf.len() - new_elements_below_cap..]); } // cursor advanced c if c > 0 => { out.extend_from_slice(&self.buf[(cursor % self.buf.len())..self.cursor]); } // cursor wrapped around c if c < 0 => { out.extend_from_slice(&self.buf[(cursor % self.buf.len())..]); out.extend_from_slice(&self.buf[..self.cursor]); } _ => unreachable!("logic dictates that… yeah, you really shouldn't ever see this!"), } } } None => self.copy_all(out), }; MessageCopyState { cursor: self.cursor, buf_len: self.buf.len(), total: self.total, } } fn has_capacity(&self) -> bool { self.buf.len() < self.buf.capacity() } } /// State used to keep track of what's new since the last time message were copied. /// /// Note that due to the nature of a ring buffer, there is no guarantee that you see all messages. pub struct MessageCopyState { cursor: usize, buf_len: usize, total: usize, } prodash-28.0.0/src/progress/key.rs000064400000000000000000000216031046102023000151460ustar 00000000000000use std::ops::{Index, IndexMut}; use crate::progress::Task; /// a level in the hierarchy of key components /// /// _NOTE:_ This means we will show weird behaviour if there are more than 2^16 tasks at the same time on a level /// as multiple progress handles will manipulate the same state. pub type Level = u8; pub(crate) type Id = u16; /// A type identifying a spot in the hierarchy of `Tree` items. #[derive(Copy, Clone, Default, Hash, Eq, PartialEq, Ord, PartialOrd, Debug)] pub struct Key(Option, Option, Option, Option, Option, Option); /// Determines if a sibling is above or below in the given level of hierarchy #[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] #[allow(missing_docs)] pub enum SiblingLocation { Above, Below, AboveAndBelow, #[default] NotFound, } impl SiblingLocation { fn merge(&mut self, other: SiblingLocation) { use SiblingLocation::*; *self = match (*self, other) { (any, NotFound) => any, (NotFound, any) => any, (Above, Below) => AboveAndBelow, (Below, Above) => AboveAndBelow, (AboveAndBelow, _) => AboveAndBelow, (_, AboveAndBelow) => AboveAndBelow, (Above, Above) => Above, (Below, Below) => Below, }; } } /// A type providing information about what's above and below `Tree` items. #[derive(Copy, Clone, Default, Eq, PartialEq, Ord, PartialOrd, Debug)] pub struct Adjacency( pub SiblingLocation, pub SiblingLocation, pub SiblingLocation, pub SiblingLocation, pub SiblingLocation, pub SiblingLocation, ); impl Adjacency { /// Return the level at which this sibling is located in the hierarchy. pub fn level(&self) -> Level { use SiblingLocation::*; match self { Adjacency(NotFound, NotFound, NotFound, NotFound, NotFound, NotFound) => 0, Adjacency(_a, NotFound, NotFound, NotFound, NotFound, NotFound) => 1, Adjacency(_a, _b, NotFound, NotFound, NotFound, NotFound) => 2, Adjacency(_a, _b, _c, NotFound, NotFound, NotFound) => 3, Adjacency(_a, _b, _c, _d, NotFound, NotFound) => 4, Adjacency(_a, _b, _c, _d, _e, NotFound) => 5, Adjacency(_a, _b, _c, _d, _e, _f) => 6, } } /// Get a reference to the sibling location at `level`. pub fn get(&self, level: Level) -> Option<&SiblingLocation> { Some(match level { 1 => &self.0, 2 => &self.1, 3 => &self.2, 4 => &self.3, 5 => &self.4, 6 => &self.5, _ => return None, }) } /// Get a mutable reference to the sibling location at `level`. pub fn get_mut(&mut self, level: Level) -> Option<&mut SiblingLocation> { Some(match level { 1 => &mut self.0, 2 => &mut self.1, 3 => &mut self.2, 4 => &mut self.3, 5 => &mut self.4, 6 => &mut self.5, _ => return None, }) } } impl Index for Adjacency { type Output = SiblingLocation; fn index(&self, index: Level) -> &Self::Output { self.get(index).expect("adjacency index in bound") } } impl IndexMut for Adjacency { fn index_mut(&mut self, index: Level) -> &mut Self::Output { self.get_mut(index).expect("adjacency index in bound") } } impl Key { /// Return the key to the child identified by `child_id` located in a new nesting level below `self`. pub fn add_child(self, child_id: Id) -> Key { match self { Key(None, None, None, None, None, None) => Key(Some(child_id), None, None, None, None, None), Key(a, None, None, None, None, None) => Key(a, Some(child_id), None, None, None, None), Key(a, b, None, None, None, None) => Key(a, b, Some(child_id), None, None, None), Key(a, b, c, None, None, None) => Key(a, b, c, Some(child_id), None, None), Key(a, b, c, d, None, None) => Key(a, b, c, d, Some(child_id), None), Key(a, b, c, d, e, _f) => { crate::warn!("Maximum nesting level reached. Adding tasks to current parent"); Key(a, b, c, d, e, Some(child_id)) } } } /// The level of hierarchy a node is placed in, i.e. the amount of path components pub fn level(&self) -> Level { match self { Key(None, None, None, None, None, None) => 0, Key(Some(_), None, None, None, None, None) => 1, Key(Some(_), Some(_), None, None, None, None) => 2, Key(Some(_), Some(_), Some(_), None, None, None) => 3, Key(Some(_), Some(_), Some(_), Some(_), None, None) => 4, Key(Some(_), Some(_), Some(_), Some(_), Some(_), None) => 5, Key(Some(_), Some(_), Some(_), Some(_), Some(_), Some(_)) => 6, _ => unreachable!("This is a bug - Keys follow a certain pattern"), } } /// Return the identifier for the item at `level`. fn get(&self, level: Level) -> Option<&Id> { match level { 1 => self.0.as_ref(), 2 => self.1.as_ref(), 3 => self.2.as_ref(), 4 => self.3.as_ref(), 5 => self.4.as_ref(), 6 => self.5.as_ref(), _ => None, } } /// Return true if the item identified by `other` shares the parent at `parent_level`. pub fn shares_parent_with(&self, other: &Key, parent_level: Level) -> bool { if parent_level < 1 { return true; } for level in 1..=parent_level { if let (Some(lhs), Some(rhs)) = (self.get(level), other.get(level)) { if lhs != rhs { return false; } } else { return false; } } true } /// Compute the adjacency map for the key in `sorted` at the given `index`. /// /// It's vital that the invariant of `sorted` to actually be sorted by key is upheld /// for the result to be reliable. pub fn adjacency(sorted: &[(Key, Task)], index: usize) -> Adjacency { use SiblingLocation::*; let key = &sorted[index].0; let key_level = key.level(); let mut adjecency = Adjacency::default(); if key_level == 0 { return adjecency; } fn search<'a>( iter: impl Iterator, key: &Key, key_level: Level, current_level: Level, _id_at_level: Id, ) -> Option { iter.map(|(k, _)| k) .take_while(|other| key.shares_parent_with(other, current_level.saturating_sub(1))) .enumerate() .find(|(_idx, k)| { if current_level == key_level { k.level() == key_level || k.level() + 1 == key_level } else { k.level() == current_level } }) .map(|(idx, _)| idx) } let upward_iter = |from: usize, key: &Key, level: Level, id_at_level: Id| { search(sorted[..from].iter().rev(), key, key_level, level, id_at_level) }; let downward_iter = |from: usize, key: &Key, level: Level, id_at_level: Id| { sorted .get(from + 1..) .and_then(|s| search(s.iter(), key, key_level, level, id_at_level)) }; { let mut cursor = index; for level in (1..=key_level).rev() { if level == 1 { adjecency[level].merge(Above); // the root or any other sibling on level one continue; } if let Some(key_offset) = upward_iter(cursor, key, level, key[level]) { cursor = index.saturating_sub(key_offset); adjecency[level].merge(Above); } } } { let mut cursor = index; for level in (1..=key_level).rev() { if let Some(key_offset) = downward_iter(cursor, key, level, key[level]) { cursor = index + key_offset; adjecency[level].merge(Below); } } } for level in 1..key_level { if key_level == 1 && index + 1 == sorted.len() { continue; } adjecency[level] = match adjecency[level] { Above | Below | NotFound => NotFound, AboveAndBelow => AboveAndBelow, }; } adjecency } /// The maximum amount of path components we can represent. pub const fn max_level() -> Level { 6 } } impl Index for Key { type Output = Id; fn index(&self, index: Level) -> &Self::Output { self.get(index).expect("key index in bound") } } prodash-28.0.0/src/progress/log.rs000064400000000000000000000105621046102023000151410ustar 00000000000000use std::{ sync::{ atomic::{AtomicBool, Ordering}, Arc, }, time::Duration, }; use crate::{ messages::MessageLevel, progress::{Id, Step, StepShared}, Count, NestedProgress, Progress, Unit, }; /// A [`NestedProgress`] implementation which displays progress as it happens without the use of a renderer. /// /// Note that this incurs considerable performance cost as each progress calls ends up getting the system time /// to see if progress information should actually be emitted. pub struct Log { name: String, id: Id, max: Option, unit: Option, step: StepShared, current_level: usize, max_level: usize, trigger: Arc, } const EMIT_LOG_EVERY_S: f32 = 0.5; const SEP: &str = "::"; impl Log { /// Create a new instance from `name` while displaying progress information only up to `max_level`. pub fn new(name: impl Into, max_level: Option) -> Self { let trigger = Arc::new(AtomicBool::new(true)); std::thread::spawn({ let duration = Duration::from_secs_f32(EMIT_LOG_EVERY_S); let trigger = Arc::downgrade(&trigger); move || { while let Some(t) = trigger.upgrade() { t.store(true, Ordering::Relaxed); std::thread::sleep(duration); } } }); Log { name: name.into(), id: crate::progress::UNKNOWN, current_level: 0, max_level: max_level.unwrap_or(usize::MAX), max: None, step: Default::default(), unit: None, trigger, } } } impl Log { fn maybe_log(&self) { if self.current_level > self.max_level { return; } let step = self.step(); if self.trigger.swap(false, Ordering::Relaxed) { match (self.max, &self.unit) { (max, Some(unit)) => log::info!("{} → {}", self.name, unit.display(step, max, None)), (Some(max), None) => log::info!("{} → {} / {}", self.name, step, max), (None, None) => log::info!("{} → {}", self.name, step), } } } } impl Count for Log { fn set(&self, step: Step) { self.step.store(step, Ordering::SeqCst); self.maybe_log() } fn step(&self) -> usize { self.step.load(Ordering::Relaxed) } fn inc_by(&self, step: Step) { self.step.fetch_add(step, Ordering::Relaxed); self.maybe_log() } fn counter(&self) -> StepShared { self.step.clone() } } impl Progress for Log { fn init(&mut self, max: Option, unit: Option) { self.max = max; self.unit = unit; } fn unit(&self) -> Option { self.unit.clone() } fn max(&self) -> Option { self.max } fn set_max(&mut self, max: Option) -> Option { let prev = self.max; self.max = max; prev } fn set_name(&mut self, name: String) { self.name = self .name .split("::") .next() .map(|parent| format!("{}{}{}", parent.to_owned(), SEP, name)) .unwrap_or(name); } fn name(&self) -> Option { self.name.split(SEP).nth(1).map(ToOwned::to_owned) } fn id(&self) -> Id { self.id } fn message(&self, level: MessageLevel, message: String) { match level { MessageLevel::Info => log::info!("ℹ{} → {}", self.name, message), MessageLevel::Failure => log::error!("𐄂{} → {}", self.name, message), MessageLevel::Success => log::info!("✓{} → {}", self.name, message), } } } impl NestedProgress for Log { type SubProgress = Log; fn add_child(&mut self, name: impl Into) -> Self::SubProgress { self.add_child_with_id(name, crate::progress::UNKNOWN) } fn add_child_with_id(&mut self, name: impl Into, id: Id) -> Self::SubProgress { Log { name: format!("{}{}{}", self.name, SEP, Into::::into(name)), id, current_level: self.current_level + 1, max_level: self.max_level, step: Default::default(), max: None, unit: None, trigger: Arc::clone(&self.trigger), } } } prodash-28.0.0/src/progress/mod.rs000064400000000000000000000063731046102023000151440ustar 00000000000000use std::{ sync::{ atomic::{AtomicUsize, Ordering}, Arc, }, time::SystemTime, }; use crate::unit::Unit; /// pub mod key; #[doc(inline)] pub use key::Key; mod utils; #[cfg(feature = "progress-log")] mod log; pub use utils::{Discard, DoOrDiscard, Either, ThroughputOnDrop}; #[cfg(feature = "progress-log")] pub use self::log::Log; /// Four bytes of function-local unique and stable identifier for each item added as progress, /// like b"TREE" or b"FILE". /// /// Note that uniqueness only relates to one particular method call where those interested in its progress /// may assume certain stable ids to look for when selecting specific bits of progress to process. pub type Id = [u8; 4]; /// The default Id to use if there is no need for an id. /// /// This is the default unless applications wish to make themselves more introspectable. pub const UNKNOWN: Id = *b"\0\0\0\0"; /// The amount of steps a progress can make pub type Step = usize; /// The amount of steps a progress can make, for threadsafe counting. pub type AtomicStep = AtomicUsize; /// As step, but shareable. pub type StepShared = Arc; /// Indicate whether a progress can or cannot be made. #[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)] pub enum State { /// Indicates a task is blocked and cannot indicate progress, optionally until the /// given time. The task cannot easily be interrupted. Blocked(&'static str, Option), /// Indicates a task cannot indicate progress, optionally until the /// given time. The task can be interrupted. Halted(&'static str, Option), /// The task is running #[default] Running, } /// Progress associated with some item in the progress tree. #[derive(Clone, Default, Debug)] pub struct Value { /// The amount of progress currently made pub step: StepShared, /// The step at which no further progress has to be made. /// /// If unset, the progress is unbounded. pub done_at: Option, /// The unit associated with the progress. pub unit: Option, /// Whether progress can be made or not pub state: State, } impl std::hash::Hash for Value { fn hash(&self, state: &mut H) { let Self { step, done_at, unit, state: our_state, } = self; done_at.hash(state); unit.hash(state); our_state.hash(state); step.load(Ordering::Relaxed).hash(state); } } impl Value { /// Returns a number between `Some(0.0)` and `Some(1.0)`, or `None` if the progress is unbounded. /// /// A task half done would return `Some(0.5)`. pub fn fraction(&self) -> Option { self.done_at .map(|done_at| self.step.load(Ordering::SeqCst) as f32 / done_at as f32) } } /// The value associated with a spot in the hierarchy. #[derive(Clone, Default, Debug, Hash)] pub struct Task { /// The name of the `Item` or task. pub name: String, /// The stable identifier of this task. /// Useful for selecting specific tasks out of a set of them. pub id: Id, /// The progress itself, unless this value belongs to an `Item` serving as organizational unit. pub progress: Option, } prodash-28.0.0/src/progress/utils.rs000064400000000000000000000213621046102023000155200ustar 00000000000000use crate::{messages::MessageLevel, progress::Id, Count, NestedProgress, Progress, Unit}; use std::sync::atomic::AtomicUsize; use std::sync::Arc; /// An implementation of [`NestedProgress`] which discards all calls. pub struct Discard; impl Count for Discard { fn set(&self, _step: usize) {} fn step(&self) -> usize { 0 } fn inc_by(&self, _step: usize) {} fn counter(&self) -> StepShared { Arc::new(AtomicUsize::default()) } } impl Progress for Discard { fn init(&mut self, _max: Option, _unit: Option) {} fn set_max(&mut self, _max: Option) -> Option { None } fn set_name(&mut self, _name: String) {} fn name(&self) -> Option { None } fn id(&self) -> Id { crate::progress::UNKNOWN } fn message(&self, _level: MessageLevel, _message: String) {} } impl NestedProgress for Discard { type SubProgress = Self; fn add_child(&mut self, _name: impl Into) -> Self { Discard } fn add_child_with_id(&mut self, _name: impl Into, _id: Id) -> Self { Discard } } /// An implementation of [`NestedProgress`] showing either one or the other implementation. /// /// Useful in conjunction with [`Discard`] and a working implementation, making it as a form of `Option` which /// can be passed to methods requiring `impl Progress`. /// See [`DoOrDiscard`] for an incarnation of this. #[allow(missing_docs)] pub enum Either { Left(L), Right(R), } impl Count for Either where L: Count, R: Count, { fn set(&self, step: usize) { match self { Either::Left(l) => l.set(step), Either::Right(r) => r.set(step), } } fn step(&self) -> usize { match self { Either::Left(l) => l.step(), Either::Right(r) => r.step(), } } fn inc_by(&self, step: usize) { match self { Either::Left(l) => l.inc_by(step), Either::Right(r) => r.inc_by(step), } } fn counter(&self) -> StepShared { match self { Either::Left(l) => l.counter(), Either::Right(r) => r.counter(), } } } impl Progress for Either where L: Progress, R: Progress, { fn init(&mut self, max: Option, unit: Option) { match self { Either::Left(l) => l.init(max, unit), Either::Right(r) => r.init(max, unit), } } fn unit(&self) -> Option { match self { Either::Left(l) => l.unit(), Either::Right(r) => r.unit(), } } fn max(&self) -> Option { match self { Either::Left(l) => l.max(), Either::Right(r) => r.max(), } } fn set_max(&mut self, max: Option) -> Option { match self { Either::Left(l) => l.set_max(max), Either::Right(r) => r.set_max(max), } } fn set_name(&mut self, name: String) { match self { Either::Left(l) => l.set_name(name), Either::Right(r) => r.set_name(name), } } fn name(&self) -> Option { match self { Either::Left(l) => l.name(), Either::Right(r) => r.name(), } } fn id(&self) -> Id { match self { Either::Left(l) => l.id(), Either::Right(r) => r.id(), } } fn message(&self, level: MessageLevel, message: String) { match self { Either::Left(l) => l.message(level, message), Either::Right(r) => r.message(level, message), } } } impl NestedProgress for Either where L: NestedProgress, R: NestedProgress, { type SubProgress = Either; fn add_child(&mut self, name: impl Into) -> Self::SubProgress { match self { Either::Left(l) => Either::Left(l.add_child(name)), Either::Right(r) => Either::Right(r.add_child(name)), } } fn add_child_with_id(&mut self, name: impl Into, id: Id) -> Self::SubProgress { match self { Either::Left(l) => Either::Left(l.add_child_with_id(name, id)), Either::Right(r) => Either::Right(r.add_child_with_id(name, id)), } } } /// An implementation of `Progress` which can be created easily from `Option`. pub struct DoOrDiscard(Either); impl From> for DoOrDiscard where T: NestedProgress, { fn from(p: Option) -> Self { match p { Some(p) => DoOrDiscard(Either::Left(p)), None => DoOrDiscard(Either::Right(Discard)), } } } impl DoOrDiscard { /// Obtain either the original [`NestedProgress`] implementation or `None`. pub fn into_inner(self) -> Option { match self { DoOrDiscard(Either::Left(p)) => Some(p), DoOrDiscard(Either::Right(_)) => None, } } /// Take out the implementation of [`NestedProgress`] and replace it with [`Discard`]. pub fn take(&mut self) -> Option { let this = std::mem::replace(self, DoOrDiscard::from(None)); match this { DoOrDiscard(Either::Left(p)) => Some(p), DoOrDiscard(Either::Right(_)) => None, } } } impl Count for DoOrDiscard where T: Count, { fn set(&self, step: usize) { self.0.set(step) } fn step(&self) -> usize { self.0.step() } fn inc_by(&self, step: usize) { self.0.inc_by(step) } fn counter(&self) -> StepShared { self.0.counter() } } impl Progress for DoOrDiscard where T: Progress, { fn init(&mut self, max: Option, unit: Option) { self.0.init(max, unit) } fn unit(&self) -> Option { self.0.unit() } fn max(&self) -> Option { self.0.max() } fn set_max(&mut self, max: Option) -> Option { self.0.set_max(max) } fn set_name(&mut self, name: String) { self.0.set_name(name); } fn name(&self) -> Option { self.0.name() } fn id(&self) -> Id { self.0.id() } fn message(&self, level: MessageLevel, message: String) { self.0.message(level, message) } } impl NestedProgress for DoOrDiscard where T: NestedProgress, { type SubProgress = DoOrDiscard; fn add_child(&mut self, name: impl Into) -> Self::SubProgress { DoOrDiscard(self.0.add_child(name)) } fn add_child_with_id(&mut self, name: impl Into, id: Id) -> Self::SubProgress { DoOrDiscard(self.0.add_child_with_id(name, id)) } } use std::time::Instant; use crate::progress::{Step, StepShared}; /// Emit a message with throughput information when the instance is dropped. pub struct ThroughputOnDrop(T, Instant); impl ThroughputOnDrop { /// Create a new instance by providing the `inner` [`NestedProgress`] implementation. pub fn new(inner: T) -> Self { ThroughputOnDrop(inner, Instant::now()) } } impl Count for ThroughputOnDrop { fn set(&self, step: usize) { self.0.set(step) } fn step(&self) -> usize { self.0.step() } fn inc_by(&self, step: usize) { self.0.inc_by(step) } fn counter(&self) -> StepShared { self.0.counter() } } impl Progress for ThroughputOnDrop { fn init(&mut self, max: Option, unit: Option) { self.0.init(max, unit) } fn unit(&self) -> Option { self.0.unit() } fn max(&self) -> Option { self.0.max() } fn set_max(&mut self, max: Option) -> Option { self.0.set_max(max) } fn set_name(&mut self, name: String) { self.0.set_name(name) } fn name(&self) -> Option { self.0.name() } fn id(&self) -> Id { self.0.id() } fn message(&self, level: MessageLevel, message: String) { self.0.message(level, message) } } impl NestedProgress for ThroughputOnDrop { type SubProgress = ThroughputOnDrop; fn add_child(&mut self, name: impl Into) -> Self::SubProgress { ThroughputOnDrop::new(self.0.add_child(name)) } fn add_child_with_id(&mut self, name: impl Into, id: Id) -> Self::SubProgress { ThroughputOnDrop::new(self.0.add_child_with_id(name, id)) } } impl Drop for ThroughputOnDrop { fn drop(&mut self) { self.0.show_throughput(self.1) } } prodash-28.0.0/src/render/line/draw.rs000064400000000000000000000302171046102023000156560ustar 00000000000000use std::{ collections::{hash_map::DefaultHasher, VecDeque}, hash::{Hash, Hasher}, io, ops::RangeInclusive, sync::atomic::Ordering, }; use crosstermion::{ ansi_term::{ANSIString, ANSIStrings, Color, Style}, color, }; use unicode_width::UnicodeWidthStr; use crate::{ messages::{Message, MessageCopyState, MessageLevel}, progress::{self, Value}, unit, Root, Throughput, }; #[derive(Default)] pub struct State { tree: Vec<(progress::Key, progress::Task)>, tree_hash: u64, messages: Vec, for_next_copy: Option, /// The size of the message origin, tracking the terminal height so things potentially off screen don't influence width anymore. message_origin_size: VecDeque, /// The maximum progress midpoint (point till progress bar starts) seen at the last tick last_progress_midpoint: Option, /// The amount of blocks per line we have written last time. blocks_per_line: VecDeque, pub throughput: Option, } impl State { pub(crate) fn update_from_progress(&mut self, progress: &impl Root) -> bool { progress.sorted_snapshot(&mut self.tree); let mut hasher = DefaultHasher::new(); self.tree.hash(&mut hasher); let cur_hash = hasher.finish(); self.for_next_copy = progress .copy_new_messages(&mut self.messages, self.for_next_copy.take()) .into(); let changed = self.tree_hash != cur_hash; self.tree_hash = cur_hash; changed } pub(crate) fn clear(&mut self) { self.tree.clear(); self.messages.clear(); self.for_next_copy.take(); } } pub struct Options { pub level_filter: Option>, pub terminal_dimensions: (u16, u16), pub keep_running_if_progress_is_empty: bool, pub output_is_terminal: bool, pub colored: bool, pub timestamp: bool, pub hide_cursor: bool, } fn messages( out: &mut impl io::Write, state: &mut State, colored: bool, max_height: usize, timestamp: bool, ) -> io::Result<()> { let mut brush = color::Brush::new(colored); fn to_color(level: MessageLevel) -> Color { use crate::messages::MessageLevel::*; match level { Info => Color::White, Success => Color::Green, Failure => Color::Red, } } let mut tokens: Vec> = Vec::with_capacity(6); let mut current_maximum = state.message_origin_size.iter().max().cloned().unwrap_or(0); for Message { time, level, origin, message, } in &state.messages { tokens.clear(); let blocks_drawn_during_previous_tick = state.blocks_per_line.pop_front().unwrap_or(0); let message_block_len = origin.width(); current_maximum = current_maximum.max(message_block_len); if state.message_origin_size.len() == max_height { state.message_origin_size.pop_front(); } state.message_origin_size.push_back(message_block_len); let color = to_color(*level); tokens.push(" ".into()); if timestamp { tokens.push( brush .style(color.dimmed().on(Color::Yellow)) .paint(crate::time::format_time_for_messages(*time)), ); tokens.push(Style::default().paint(" ")); } else { tokens.push("".into()); }; tokens.push(brush.style(Style::default().dimmed()).paint(format!( "{:>fill_size$}{}", "", origin, fill_size = current_maximum - message_block_len, ))); tokens.push(" ".into()); tokens.push(brush.style(color.bold()).paint(message)); let message_block_count = block_count_sans_ansi_codes(&tokens); write!(out, "{}", ANSIStrings(tokens.as_slice()))?; if blocks_drawn_during_previous_tick > message_block_count { newline_with_overdraw(out, &tokens, blocks_drawn_during_previous_tick)?; } else { writeln!(out)?; } } Ok(()) } pub fn all(out: &mut impl io::Write, show_progress: bool, state: &mut State, config: &Options) -> io::Result<()> { if !config.keep_running_if_progress_is_empty && state.tree.is_empty() { return Err(io::Error::new(io::ErrorKind::Other, "stop as progress is empty")); } messages( out, state, config.colored, config.terminal_dimensions.1 as usize, config.timestamp, )?; if show_progress && config.output_is_terminal { if let Some(tp) = state.throughput.as_mut() { tp.update_elapsed(); } let level_range = config .level_filter .clone() .unwrap_or(RangeInclusive::new(0, progress::key::Level::max_value())); let lines_to_be_drawn = state .tree .iter() .filter(|(k, _)| level_range.contains(&k.level())) .count(); if state.blocks_per_line.len() < lines_to_be_drawn { state.blocks_per_line.resize(lines_to_be_drawn, 0); } let mut tokens: Vec> = Vec::with_capacity(4); let mut max_midpoint = 0; for ((key, value), ref mut blocks_in_last_iteration) in state .tree .iter() .filter(|(k, _)| level_range.contains(&k.level())) .zip(state.blocks_per_line.iter_mut()) { max_midpoint = max_midpoint.max( format_progress( key, value, config.terminal_dimensions.0, config.colored, state.last_progress_midpoint, state .throughput .as_mut() .and_then(|tp| tp.update_and_get(key, value.progress.as_ref())), &mut tokens, ) .unwrap_or(0), ); write!(out, "{}", ANSIStrings(tokens.as_slice()))?; **blocks_in_last_iteration = newline_with_overdraw(out, &tokens, **blocks_in_last_iteration)?; } if let Some(tp) = state.throughput.as_mut() { tp.reconcile(&state.tree); } state.last_progress_midpoint = Some(max_midpoint); // overwrite remaining lines that we didn't touch naturally let lines_drawn = lines_to_be_drawn; if state.blocks_per_line.len() > lines_drawn { for blocks_in_last_iteration in state.blocks_per_line.iter().skip(lines_drawn) { writeln!(out, "{:>width$}", "", width = *blocks_in_last_iteration as usize)?; } // Move cursor back to end of the portion we have actually drawn crosstermion::execute!(out, crosstermion::cursor::MoveUp(state.blocks_per_line.len() as u16))?; state.blocks_per_line.resize(lines_drawn, 0); } else if lines_drawn > 0 { crosstermion::execute!(out, crosstermion::cursor::MoveUp(lines_drawn as u16))?; } } Ok(()) } /// Must be called directly after `tokens` were drawn, without newline. Takes care of adding the newline. fn newline_with_overdraw( out: &mut impl io::Write, tokens: &[ANSIString<'_>], blocks_in_last_iteration: u16, ) -> io::Result { let current_block_count = block_count_sans_ansi_codes(tokens); if blocks_in_last_iteration > current_block_count { // fill to the end of line to overwrite what was previously there writeln!( out, "{:>width$}", "", width = (blocks_in_last_iteration - current_block_count) as usize )?; } else { writeln!(out)?; }; Ok(current_block_count) } fn block_count_sans_ansi_codes(strings: &[ANSIString<'_>]) -> u16 { strings.iter().map(|s| s.width() as u16).sum() } fn draw_progress_bar(p: &Value, style: Style, mut blocks_available: u16, colored: bool, buf: &mut Vec>) { let mut brush = color::Brush::new(colored); let styled_brush = brush.style(style); blocks_available = blocks_available.saturating_sub(3); // account for…I don't really know it's magic buf.push(" [".into()); match p.fraction() { Some(mut fraction) => { fraction = fraction.min(1.0); blocks_available = blocks_available.saturating_sub(1); // account for '>' apparently let progress_blocks = (blocks_available as f32 * fraction).floor() as usize; buf.push(styled_brush.paint(format!("{:=")); buf.push(styled_brush.style(style.dimmed()).paint(format!( "{:- { const CHARS: [char; 6] = ['=', '=', '=', ' ', ' ', ' ']; buf.push( styled_brush.paint( (p.step.load(Ordering::SeqCst)..std::usize::MAX) .take(blocks_available as usize) .map(|idx| CHARS[idx % CHARS.len()]) .rev() .collect::(), ), ); } } buf.push("]".into()); } fn progress_style(p: &Value) -> Style { use crate::progress::State::*; match p.state { Running => if let Some(fraction) = p.fraction() { if fraction > 0.8 { Color::Green } else { Color::Yellow } } else { Color::White } .normal(), Halted(_, _) => Color::Red.dimmed(), Blocked(_, _) => Color::Red.normal(), } } fn format_progress<'a>( key: &progress::Key, value: &'a progress::Task, column_count: u16, colored: bool, midpoint: Option, throughput: Option, buf: &mut Vec>, ) -> Option { let mut brush = color::Brush::new(colored); buf.clear(); buf.push(Style::new().paint(format!("{:>level$}", "", level = key.level() as usize))); match value.progress.as_ref() { Some(progress) => { let style = progress_style(progress); buf.push(brush.style(Color::Cyan.bold()).paint(&value.name)); buf.push(" ".into()); let pre_unit = buf.len(); let values_brush = brush.style(Style::new().bold().dimmed()); match progress.unit.as_ref() { Some(unit) => { let mut display = unit.display(progress.step.load(Ordering::SeqCst), progress.done_at, throughput); buf.push(values_brush.paint(display.values().to_string())); buf.push(" ".into()); buf.push(display.unit().to_string().into()); } None => { buf.push(values_brush.paint(match progress.done_at { Some(done_at) => format!("{}/{}", progress.step.load(Ordering::SeqCst), done_at), None => format!("{}", progress.step.load(Ordering::SeqCst)), })); } } let desired_midpoint = block_count_sans_ansi_codes(buf.as_slice()); let actual_midpoint = if let Some(midpoint) = midpoint { let padding = midpoint.saturating_sub(desired_midpoint); if padding > 0 { buf.insert(pre_unit, " ".repeat(padding as usize).into()); } block_count_sans_ansi_codes(buf.as_slice()) } else { desired_midpoint }; let blocks_left = column_count.saturating_sub(actual_midpoint); if blocks_left > 0 { draw_progress_bar(progress, style, blocks_left, colored, buf); } Some(desired_midpoint) } None => { // headline only - FIXME: would have to truncate it if it is too long for the line… buf.push(brush.style(Color::White.bold()).paint(&value.name)); None } } } prodash-28.0.0/src/render/line/engine.rs000064400000000000000000000312111046102023000161610ustar 00000000000000#[cfg(feature = "signal-hook")] use std::sync::Arc; use std::{ io, ops::RangeInclusive, sync::atomic::{AtomicBool, Ordering}, time::Duration, }; use crate::{progress, render::line::draw, Throughput, WeakRoot}; /// Options used for configuring a [line renderer][render()]. #[derive(Clone)] pub struct Options { /// If true, _(default true)_, we assume the output stream belongs to a terminal. /// /// If false, we won't print any live progress, only log messages. pub output_is_terminal: bool, /// If true, _(default: true)_ we will display color. You should use `output_is_terminal && crosstermion::should_colorize()` /// to determine this value. /// /// Please note that you can enforce color even if the output stream is not connected to a terminal by setting /// this field to true. pub colored: bool, /// If true, _(default: false)_, a timestamp will be shown before each message. pub timestamp: bool, /// The amount of columns and rows to use for drawing. Defaults to (80, 20). pub terminal_dimensions: (u16, u16), /// If true, _(default: false)_, the cursor will be hidden for a more visually appealing display. /// /// Please note that you must make sure the line renderer is properly shut down to restore the previous cursor /// settings. See the `signal-hook` documentation in the README for more information. pub hide_cursor: bool, /// If true, (default false), we will keep track of the previous progress state to derive /// continuous throughput information from. Throughput will only show for units which have /// explicitly enabled it, it is opt-in. /// /// This comes at the cost of additional memory and CPU time. pub throughput: bool, /// If set, specify all levels that should be shown. Otherwise all available levels are shown. /// /// This is useful to filter out high-noise lower level progress items in the tree. pub level_filter: Option>, /// If set, progress will only actually be shown after the given duration. Log messages will always be shown without delay. /// /// This option can be useful to not enforce progress for short actions, causing it to flicker. /// Please note that this won't affect display of messages, which are simply logged. pub initial_delay: Option, /// The amount of frames to draw per second. If below 1.0, it determines the amount of seconds between the frame. /// /// *e.g.* 1.0/4.0 is one frame every 4 seconds. pub frames_per_second: f32, /// If true (default: true), we will keep waiting for progress even after we encountered an empty list of drawable progress items. /// /// Please note that you should add at least one item to the `prodash::Tree` before launching the application or else /// risk a race causing nothing to be rendered at all. pub keep_running_if_progress_is_empty: bool, } /// The kind of stream to use for auto-configuration. pub enum StreamKind { /// Standard output Stdout, /// Standard error Stderr, } /// Convenience impl Options { /// Automatically configure (and overwrite) the following fields based on terminal configuration. /// /// * output_is_terminal /// * colored /// * terminal_dimensions /// * hide-cursor (based on presence of 'signal-hook' feature. #[cfg(feature = "render-line-autoconfigure")] pub fn auto_configure(mut self, output: StreamKind) -> Self { self.output_is_terminal = match output { StreamKind::Stdout => is_terminal::is_terminal(std::io::stdout()), StreamKind::Stderr => is_terminal::is_terminal(std::io::stderr()), }; self.colored = self.output_is_terminal && crosstermion::color::allowed(); self.terminal_dimensions = crosstermion::terminal::size().unwrap_or((80, 20)); #[cfg(feature = "signal-hook")] self.auto_hide_cursor(); self } #[cfg(all(feature = "render-line-autoconfigure", feature = "signal-hook"))] fn auto_hide_cursor(&mut self) { self.hide_cursor = true; } #[cfg(not(feature = "render-line-autoconfigure"))] /// No-op - only available with the `render-line-autoconfigure` feature toggle. pub fn auto_configure(self, _output: StreamKind) -> Self { self } } impl Default for Options { fn default() -> Self { Options { output_is_terminal: true, colored: true, timestamp: false, terminal_dimensions: (80, 20), hide_cursor: false, level_filter: None, initial_delay: None, frames_per_second: 6.0, throughput: false, keep_running_if_progress_is_empty: true, } } } /// A handle to the render thread, which when dropped will instruct it to stop showing progress. pub struct JoinHandle { inner: Option>>, connection: std::sync::mpsc::SyncSender, // If we disconnect before sending a Quit event, the selector continuously informs about the 'Disconnect' state disconnected: bool, } impl JoinHandle { /// `detach()` and `forget()` to remove any effects associated with this handle. pub fn detach(mut self) { self.disconnect(); self.forget(); } /// Remove the handles capability to instruct the render thread to stop, but it will still wait for it /// if dropped. /// Use `forget()` if it should not wait for the render thread anymore. pub fn disconnect(&mut self) { self.disconnected = true; } /// Remove the handles capability to `join()` by forgetting the threads handle pub fn forget(&mut self) { self.inner.take(); } /// Wait for the thread to shutdown naturally, for example because there is no more progress to display pub fn wait(mut self) { self.inner.take().and_then(|h| h.join().ok()); } /// Send the shutdown signal right after one last redraw pub fn shutdown(&mut self) { if !self.disconnected { self.connection.send(Event::Tick).ok(); self.connection.send(Event::Quit).ok(); } } /// Send the signal to shutdown and wait for the thread to be shutdown. pub fn shutdown_and_wait(mut self) { self.shutdown(); self.wait(); } } impl Drop for JoinHandle { fn drop(&mut self) { self.shutdown(); self.inner.take().and_then(|h| h.join().ok()); } } #[derive(Debug)] enum Event { Tick, Quit, #[cfg(feature = "signal-hook")] Resize(u16, u16), } /// Write a line-based representation of `progress` to `out` which is assumed to be a terminal. /// /// Configure it with `config`, see the [`Options`] for details. pub fn render( mut out: impl io::Write + Send + 'static, progress: impl WeakRoot + Send + 'static, Options { output_is_terminal, colored, timestamp, level_filter, terminal_dimensions, initial_delay, frames_per_second, keep_running_if_progress_is_empty, hide_cursor, throughput, }: Options, ) -> JoinHandle { #[cfg_attr(not(feature = "signal-hook"), allow(unused_mut))] let mut config = draw::Options { level_filter, terminal_dimensions, keep_running_if_progress_is_empty, output_is_terminal, colored, timestamp, hide_cursor, }; let (event_send, event_recv) = std::sync::mpsc::sync_channel::(1); let show_cursor = possibly_hide_cursor(&mut out, hide_cursor && output_is_terminal); static SHOW_PROGRESS: AtomicBool = AtomicBool::new(false); #[cfg(feature = "signal-hook")] let term_signal_received: Arc = Arc::new(AtomicBool::new(false)); #[cfg(feature = "signal-hook")] let terminal_resized: Arc = Arc::new(AtomicBool::new(false)); #[cfg(feature = "signal-hook")] { for sig in signal_hook::consts::TERM_SIGNALS { signal_hook::flag::register(*sig, term_signal_received.clone()).ok(); } #[cfg(unix)] signal_hook::flag::register(signal_hook::consts::SIGWINCH, terminal_resized.clone()).ok(); } let handle = std::thread::Builder::new() .name("render-line-eventloop".into()) .spawn({ let tick_send = event_send.clone(); move || { { let initial_delay = initial_delay.unwrap_or_default(); SHOW_PROGRESS.store(initial_delay == Duration::default(), Ordering::Relaxed); if !SHOW_PROGRESS.load(Ordering::Relaxed) { std::thread::Builder::new() .name("render-line-progress-delay".into()) .spawn(move || { std::thread::sleep(initial_delay); SHOW_PROGRESS.store(true, Ordering::Relaxed); }) .ok(); } } let mut state = draw::State::default(); if throughput { state.throughput = Some(Throughput::default()); } let secs = 1.0 / frames_per_second; let _ticker = std::thread::Builder::new() .name("render-line-ticker".into()) .spawn(move || loop { #[cfg(feature = "signal-hook")] { if term_signal_received.load(Ordering::SeqCst) { tick_send.send(Event::Quit).ok(); break; } if terminal_resized.load(Ordering::SeqCst) { terminal_resized.store(false, Ordering::SeqCst); if let Ok((x, y)) = crosstermion::terminal::size() { tick_send.send(Event::Resize(x, y)).ok(); } } } if tick_send.send(Event::Tick).is_err() { break; } std::thread::sleep(Duration::from_secs_f32(secs)); }) .expect("starting a thread works"); for event in event_recv { match event { #[cfg(feature = "signal-hook")] Event::Resize(x, y) => { config.terminal_dimensions = (x, y); draw::all(&mut out, SHOW_PROGRESS.load(Ordering::Relaxed), &mut state, &config)?; } Event::Tick => match progress.upgrade() { Some(progress) => { let has_changed = state.update_from_progress(&progress); draw::all( &mut out, SHOW_PROGRESS.load(Ordering::Relaxed) && has_changed, &mut state, &config, )?; } None => { state.clear(); draw::all(&mut out, SHOW_PROGRESS.load(Ordering::Relaxed), &mut state, &config)?; break; } }, Event::Quit => { state.clear(); draw::all(&mut out, SHOW_PROGRESS.load(Ordering::Relaxed), &mut state, &config)?; break; } } } if show_cursor { crosstermion::execute!(out, crosstermion::cursor::Show).ok(); } // One day we might try this out on windows, but let's not risk it now. #[cfg(unix)] write!(out, "\x1b[2K\r").ok(); // clear the last line. Ok(()) } }) .expect("starting a thread works"); JoinHandle { inner: Some(handle), connection: event_send, disconnected: false, } } // Not all configurations actually need it to be mut, but those with the 'signal-hook' feature do #[allow(unused_mut)] fn possibly_hide_cursor(out: &mut impl io::Write, mut hide_cursor: bool) -> bool { if hide_cursor { crosstermion::execute!(out, crosstermion::cursor::Hide).is_ok() } else { false } } prodash-28.0.0/src/render/line/mod.rs000064400000000000000000000005001046102023000154700ustar 00000000000000#[cfg(all( feature = "render-line", not(any(feature = "render-line-crossterm", feature = "render-line-termion")) ))] compile_error!("Please choose either one of these features: 'render-line-crossterm' or 'render-line-termion'"); mod draw; mod engine; pub use engine::{render, JoinHandle, Options, StreamKind}; prodash-28.0.0/src/render/mod.rs000064400000000000000000000003501046102023000145440ustar 00000000000000#[cfg(feature = "render-tui")] /// pub mod tui; #[cfg(feature = "render-tui")] pub use self::tui::render as tui; #[cfg(feature = "render-line")] /// pub mod line; #[cfg(feature = "render-line")] pub use self::line::render as line; prodash-28.0.0/src/render/tui/draw/all.rs000064400000000000000000000111411046102023000162730ustar 00000000000000use std::time::Duration; use tui::{ buffer::Buffer, layout::Rect, style::{Modifier, Style}, text::Span, widgets::{Block, Borders, Widget}, }; use crate::{ messages::Message, progress::{Key, Task}, render::tui::{ draw, utils::{block_width, rect}, InterruptDrawInfo, Line, }, Throughput, }; #[derive(Default)] pub struct State { pub title: String, pub task_offset: u16, pub message_offset: u16, pub hide_messages: bool, pub messages_fullscreen: bool, pub user_provided_window_size: Option, pub duration_per_frame: Duration, pub information: Vec, pub hide_info: bool, pub maximize_info: bool, pub last_tree_column_width: Option, pub next_tree_column_width: Option, pub throughput: Option, } pub(crate) fn all( state: &mut State, interrupt_mode: InterruptDrawInfo, entries: &[(Key, Task)], messages: &[Message], bound: Rect, buf: &mut Buffer, ) { let (bound, info_pane) = compute_info_bound( bound, if state.hide_info { &[] } else { &state.information }, state.maximize_info, ); let bold = Style::default().add_modifier(Modifier::BOLD); let window = Block::default() .title(Span::styled(state.title.as_str(), bold)) .borders(Borders::ALL); let inner_area = window.inner(bound); window.render(bound, buf); if bound.width < 4 || bound.height < 4 { return; } let border_width = 1; draw::progress::headline( entries, interrupt_mode, state.duration_per_frame, buf, rect::offset_x( Rect { height: 1, width: bound.width.saturating_sub(border_width), ..bound }, block_width(&state.title) + (border_width * 2), ), ); let (progress_pane, messages_pane) = compute_pane_bounds( if state.hide_messages { &[] } else { messages }, inner_area, state.messages_fullscreen, ); draw::progress::pane(entries, progress_pane, buf, state); if let Some(messages_pane) = messages_pane { draw::messages::pane( messages, messages_pane, Rect { width: messages_pane.width + 2, ..rect::line_bound(bound, bound.height.saturating_sub(1) as usize) }, &mut state.message_offset, buf, ); } if let Some(info_pane) = info_pane { draw::information::pane(&state.information, info_pane, buf); } } fn compute_pane_bounds(messages: &[Message], inner: Rect, messages_fullscreen: bool) -> (Rect, Option) { if messages.is_empty() { (inner, None) } else { let (task_percent, messages_percent) = if messages_fullscreen { (0.1, 0.9) } else { (0.75, 0.25) }; let tasks_height: u16 = (inner.height as f32 * task_percent).ceil() as u16; let messages_height: u16 = (inner.height as f32 * messages_percent).floor() as u16; if messages_height < 2 { (inner, None) } else { let messages_title = 1u16; let new_messages_height = messages_height.min((messages.len() + messages_title as usize) as u16); let tasks_height = tasks_height.saturating_add(messages_height - new_messages_height); let messages_height = new_messages_height; ( Rect { height: tasks_height, ..inner }, Some(rect::intersect( Rect { y: tasks_height + messages_title, height: messages_height, ..inner }, inner, )), ) } } } fn compute_info_bound(bound: Rect, info: &[Line], maximize: bool) -> (Rect, Option) { if info.is_empty() { return (bound, None); } let margin = 1; let max_line_width = info.iter().fold(0, |state, l| { state.max( block_width(match l { Line::Text(s) | Line::Title(s) => s, }) + margin * 2, ) }); let pane_width = if maximize { bound.width.saturating_sub(8).min(max_line_width) } else { (bound.width / 3).min(max_line_width) }; if pane_width < max_line_width / 3 { return (bound, None); } ( Rect { width: bound.width.saturating_sub(pane_width), ..bound }, Some(rect::snap_to_right(bound, pane_width)), ) } prodash-28.0.0/src/render/tui/draw/information.rs000064400000000000000000000040271046102023000200550ustar 00000000000000use tui::{ buffer::Buffer, layout::Rect, style::{Modifier, Style}, text::Span, widgets::{Block, Borders, Widget}, }; use crate::render::tui::{ utils::{block_width, draw_text_with_ellipsis_nowrap, rect}, Line, }; pub fn pane(lines: &[Line], bound: Rect, buf: &mut Buffer) { let bold = Style::default().add_modifier(Modifier::BOLD); let block = Block::default() .title(Span::styled("Information", bold)) .borders(Borders::TOP | Borders::BOTTOM); let inner_bound = block.inner(bound); block.render(bound, buf); let help_text = " ⨯ = [ | ▢ = { "; draw_text_with_ellipsis_nowrap(rect::snap_to_right(bound, block_width(help_text)), buf, help_text, bold); let bound = Rect { width: inner_bound.width.saturating_sub(1), ..inner_bound }; let mut offset = 0; for (line, info) in lines.windows(2).enumerate() { let (info, next_info) = (&info[0], &info[1]); let line = line + offset; if line >= bound.height as usize { break; } let line_bound = rect::line_bound(bound, line); match info { Line::Title(text) => { let blocks_drawn = draw_text_with_ellipsis_nowrap(line_bound, buf, text, bold); let lines_rect = rect::offset_x(line_bound, blocks_drawn + 1); for x in lines_rect.left()..lines_rect.right() { buf.get_mut(x, lines_rect.y).set_symbol("─"); } offset += 1; } Line::Text(text) => { draw_text_with_ellipsis_nowrap(rect::offset_x(line_bound, 1), buf, text, None); } }; if let Line::Title(_) = next_info { offset += 1; } } if let Some(Line::Text(text)) = lines.last() { let line = lines.len().saturating_sub(1) + offset; if line < bound.height as usize { draw_text_with_ellipsis_nowrap(rect::offset_x(rect::line_bound(bound, line), 1), buf, text, bold); } } } prodash-28.0.0/src/render/tui/draw/messages.rs000064400000000000000000000115571046102023000173450ustar 00000000000000use std::time::SystemTime; use tui::{ buffer::Buffer, layout::Rect, style::{Color, Modifier, Style}, text::Span, widgets::{Block, Borders, Widget}, }; use unicode_width::UnicodeWidthStr; use crate::{ messages::{Message, MessageLevel}, render::tui::utils::{block_width, draw_text_with_ellipsis_nowrap, rect, sanitize_offset, VERTICAL_LINE}, time::{format_time_for_messages, DATE_TIME_HMS}, }; pub fn pane(messages: &[Message], bound: Rect, overflow_bound: Rect, offset: &mut u16, buf: &mut Buffer) { let bold = Style::default().add_modifier(Modifier::BOLD); let block = Block::default() .title(Span::styled("Messages", bold)) .borders(Borders::TOP); let inner_bound = block.inner(bound); block.render(bound, buf); let help_text = " ⨯ = `| ▢ = ~ "; draw_text_with_ellipsis_nowrap(rect::snap_to_right(bound, block_width(help_text)), buf, help_text, bold); let bound = inner_bound; *offset = sanitize_offset(*offset, messages.len(), bound.height); let max_origin_width = messages .iter() .rev() .skip(*offset as usize) .take(bound.height as usize) .fold(0, |state, message| state.max(block_width(&message.origin))); for ( line, Message { time, message, level, origin, }, ) in messages .iter() .rev() .skip(*offset as usize) .take(bound.height as usize) .enumerate() { let line_bound = rect::line_bound(bound, line); let (time_bound, level_bound, origin_bound, message_bound) = compute_bounds(line_bound, max_origin_width); if let Some(time_bound) = time_bound { draw_text_with_ellipsis_nowrap(time_bound, buf, format_time_column(time), None); } if let Some(level_bound) = level_bound { draw_text_with_ellipsis_nowrap( level_bound, buf, format_level_column(*level), Some(level_to_style(*level)), ); draw_text_with_ellipsis_nowrap(rect::offset_x(level_bound, LEVEL_TEXT_WIDTH), buf, VERTICAL_LINE, None); } if let Some(origin_bound) = origin_bound { draw_text_with_ellipsis_nowrap(origin_bound, buf, origin, None); draw_text_with_ellipsis_nowrap(rect::offset_x(origin_bound, max_origin_width), buf, "→", None); } draw_text_with_ellipsis_nowrap(message_bound, buf, message, None); } if (bound.height as usize) < messages.len().saturating_sub(*offset as usize) || (*offset).min(messages.len() as u16) > 0 { let messages_below = messages .len() .saturating_sub(bound.height.saturating_add(*offset) as usize); let messages_skipped = (*offset).min(messages.len() as u16); draw_text_with_ellipsis_nowrap( rect::offset_x(overflow_bound, 1), buf, format!("… {} skipped and {} more", messages_skipped, messages_below), bold, ); let help_text = " ⇊ = D|↓ = J|⇈ = U|↑ = K ┘"; draw_text_with_ellipsis_nowrap( rect::snap_to_right(overflow_bound, block_width(help_text)), buf, help_text, bold, ); } } const LEVEL_TEXT_WIDTH: u16 = 4; fn format_level_column(level: MessageLevel) -> &'static str { use MessageLevel::*; match level { Info => "info", Failure => "fail", Success => "done", } } fn level_to_style(level: MessageLevel) -> Style { use MessageLevel::*; Style::default() .fg(Color::Black) .add_modifier(Modifier::BOLD) .bg(match level { Info => Color::White, Failure => Color::Red, Success => Color::Green, }) } fn format_time_column(time: &SystemTime) -> String { format!("{}{}", format_time_for_messages(*time), VERTICAL_LINE) } fn compute_bounds(line: Rect, max_origin_width: u16) -> (Option, Option, Option, Rect) { let vertical_line_width = VERTICAL_LINE.width() as u16; let mythical_offset_we_should_not_need = 1; let time_bound = Rect { width: DATE_TIME_HMS as u16 + vertical_line_width, ..line }; let mut cursor = time_bound.width + mythical_offset_we_should_not_need; let level_bound = Rect { x: cursor, width: LEVEL_TEXT_WIDTH + vertical_line_width, ..line }; cursor += level_bound.width; let origin_bound = Rect { x: cursor, width: max_origin_width + vertical_line_width, ..line }; cursor += origin_bound.width; let message_bound = rect::intersect(rect::offset_x(line, cursor), line); if message_bound.width < 30 { return (None, None, None, line); } (Some(time_bound), Some(level_bound), Some(origin_bound), message_bound) } prodash-28.0.0/src/render/tui/draw/mod.rs000064400000000000000000000001311046102023000162770ustar 00000000000000mod all; mod information; mod messages; mod progress; pub(crate) use all::{all, State}; prodash-28.0.0/src/render/tui/draw/progress.rs000064400000000000000000000414631046102023000174010ustar 00000000000000use std::{ fmt, sync::atomic::Ordering, time::{Duration, SystemTime}, }; use humantime::format_duration; use tui::{ buffer::Buffer, layout::Rect, style::{Color, Modifier, Style}, }; use tui_react::fill_background; use crate::{ progress::{self, Key, Step, Task, Value}, render::tui::{ draw::State, utils::{ block_width, draw_text_nowrap_fn, draw_text_with_ellipsis_nowrap, rect, sanitize_offset, GraphemeCountWriter, VERTICAL_LINE, }, InterruptDrawInfo, }, time::format_now_datetime_seconds, unit, Throughput, }; const MIN_TREE_WIDTH: u16 = 20; pub fn pane(entries: &[(Key, progress::Task)], mut bound: Rect, buf: &mut Buffer, state: &mut State) { state.task_offset = sanitize_offset(state.task_offset, entries.len(), bound.height); let needs_overflow_line = if entries.len() > bound.height as usize || (state.task_offset).min(entries.len() as u16) > 0 { bound.height = bound.height.saturating_sub(1); true } else { false }; state.task_offset = sanitize_offset(state.task_offset, entries.len(), bound.height); if entries.is_empty() { return; } let initial_column_width = bound.width / 3; let desired_max_tree_draw_width = *state.next_tree_column_width.as_ref().unwrap_or(&initial_column_width); { if initial_column_width >= MIN_TREE_WIDTH { let tree_bound = Rect { width: desired_max_tree_draw_width, ..bound }; let computed = draw_tree(entries, buf, tree_bound, state.task_offset); state.last_tree_column_width = Some(computed); } else { state.last_tree_column_width = Some(0); }; } { if let Some(tp) = state.throughput.as_mut() { tp.update_elapsed(); } let progress_area = rect::offset_x(bound, desired_max_tree_draw_width); draw_progress( entries, buf, progress_area, state.task_offset, state.throughput.as_mut(), ); if let Some(tp) = state.throughput.as_mut() { tp.reconcile(entries); } } if needs_overflow_line { let overflow_rect = Rect { y: bound.height + 1, height: 1, ..bound }; draw_overflow( entries, buf, overflow_rect, desired_max_tree_draw_width, bound.height, state.task_offset, ); } } pub(crate) fn headline( entries: &[(Key, Task)], interrupt_mode: InterruptDrawInfo, duration_per_frame: Duration, buf: &mut Buffer, bound: Rect, ) { let (num_running_tasks, num_blocked_tasks, num_groups) = entries.iter().fold( (0, 0, 0), |(mut running, mut blocked, mut groups), (_key, Task { progress, .. })| { match progress.as_ref().map(|p| p.state) { Some(progress::State::Running) => running += 1, Some(progress::State::Blocked(_, _)) | Some(progress::State::Halted(_, _)) => blocked += 1, None => groups += 1, } (running, blocked, groups) }, ); let text = format!( " {} {} {:3} running + {:3} blocked + {:3} groups = {} ", match interrupt_mode { InterruptDrawInfo::Instantly => "'q' or CTRL+c to quit", InterruptDrawInfo::Deferred(interrupt_requested) => { if interrupt_requested { "interrupt requested - please wait" } else { "cannot interrupt current operation" } } }, if duration_per_frame > Duration::from_secs(1) { format!( " Every {}s → {}", duration_per_frame.as_secs(), format_now_datetime_seconds() ) } else { "".into() }, num_running_tasks, num_blocked_tasks, num_groups, entries.len() ); let bold = Style::default().add_modifier(Modifier::BOLD); draw_text_with_ellipsis_nowrap(rect::snap_to_right(bound, block_width(&text) + 1), buf, text, bold); } struct ProgressFormat<'a>(&'a Option, u16, Option); impl<'a> fmt::Display for ProgressFormat<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.0 { Some(p) => match p.unit.as_ref() { Some(unit) => write!( f, "{}", unit.display(p.step.load(Ordering::SeqCst), p.done_at, self.2.clone()) ), None => match p.done_at { Some(done_at) => write!(f, "{}/{}", p.step.load(Ordering::SeqCst), done_at), None => write!(f, "{}", p.step.load(Ordering::SeqCst)), }, }, None => write!(f, "{:─ bool { entries .get(index + 1) .and_then(|(other_key, other_val)| { entries.get(index).map(|(cur_key, _)| { cur_key.shares_parent_with(other_key, cur_key.level()) && other_val.progress.is_some() }) }) .unwrap_or(false) } pub fn draw_progress( entries: &[(Key, Task)], buf: &mut Buffer, bound: Rect, offset: u16, mut throughput: Option<&mut Throughput>, ) { let title_spacing = 2u16 + 1; // 2 on the left, 1 on the right let max_progress_label_width = entries .iter() .skip(offset as usize) .take(bound.height as usize) .map(|(_, Task { progress, .. })| progress) .fold(0, |state, progress| match progress { progress @ Some(_) => { use std::io::Write; let mut w = GraphemeCountWriter::default(); write!(w, "{}", ProgressFormat(progress, 0, None)).expect("never fails"); state.max(w.0) } None => state, }); for ( line, ( entry_index, ( key, Task { progress, name: title, id: _, }, ), ), ) in entries .iter() .enumerate() .skip(offset as usize) .take(bound.height as usize) .enumerate() { let throughput = throughput .as_mut() .and_then(|tp| tp.update_and_get(key, progress.as_ref())); let line_bound = rect::line_bound(bound, line); let progress_text = format!( " {progress}", progress = ProgressFormat( progress, if has_child(entries, entry_index) { bound.width.saturating_sub(title_spacing) } else { 0 }, throughput ) ); draw_text_with_ellipsis_nowrap(line_bound, buf, VERTICAL_LINE, None); let tree_prefix = level_prefix(entries, entry_index); let progress_rect = rect::offset_x(line_bound, block_width(&tree_prefix)); draw_text_with_ellipsis_nowrap(line_bound, buf, tree_prefix, None); match progress .as_ref() .map(|p| (p.fraction(), p.state, p.step.load(Ordering::SeqCst))) { Some((Some(fraction), state, _step)) => { let mut progress_text = progress_text; add_block_eta(state, &mut progress_text); let (bound, style) = draw_progress_bar_fn(buf, progress_rect, fraction, |fraction| match state { progress::State::Blocked(_, _) => Color::Red, progress::State::Halted(_, _) => Color::LightRed, progress::State::Running => { if fraction >= 0.8 { Color::Green } else { Color::Yellow } } }); let style_fn = move |_t: &str, x: u16, _y: u16| { if x < bound.right() { style } else { Style::default() } }; draw_text_nowrap_fn(progress_rect, buf, progress_text, style_fn); } Some((None, state, step)) => { let mut progress_text = progress_text; add_block_eta(state, &mut progress_text); draw_text_with_ellipsis_nowrap(progress_rect, buf, progress_text, None); let bar_rect = rect::offset_x(line_bound, max_progress_label_width as u16); draw_spinner( buf, bar_rect, step, line, match state { progress::State::Blocked(_, _) => Color::Red, progress::State::Halted(_, _) => Color::LightRed, progress::State::Running => Color::White, }, ); } None => { let bold = Style::default().add_modifier(Modifier::BOLD); draw_text_nowrap_fn(progress_rect, buf, progress_text, |_, _, _| Style::default()); draw_text_with_ellipsis_nowrap(progress_rect, buf, format!(" {} ", title), bold); } } } } fn add_block_eta(state: progress::State, progress_text: &mut String) { match state { progress::State::Blocked(reason, maybe_eta) | progress::State::Halted(reason, maybe_eta) => { progress_text.push_str(" ["); progress_text.push_str(reason); progress_text.push(']'); if let Some(eta) = maybe_eta { let now = SystemTime::now(); if eta > now { use std::fmt::Write; write!( progress_text, " → {} to {}", format_duration(eta.duration_since(now).expect("computation to work")), if let progress::State::Blocked(_, _) = state { "unblock" } else { "continue" } ) .expect("in-memory writes never fail"); } } } progress::State::Running => {} } } fn draw_spinner(buf: &mut Buffer, bound: Rect, step: Step, seed: usize, color: Color) { if bound.width == 0 { return; } let x = bound.x + ((step + seed) % bound.width as usize) as u16; let width = 5; let bound = rect::intersect(Rect { x, width, ..bound }, bound); tui_react::fill_background(bound, buf, color); } fn draw_progress_bar_fn( buf: &mut Buffer, bound: Rect, fraction: f32, style: impl FnOnce(f32) -> Color, ) -> (Rect, Style) { if bound.width == 0 { return (Rect::default(), Style::default()); } let mut fractional_progress_rect = Rect { width: ((bound.width as f32 * fraction).floor() as u16).min(bound.width), ..bound }; let color = style(fraction); for y in fractional_progress_rect.top()..fractional_progress_rect.bottom() { for x in fractional_progress_rect.left()..fractional_progress_rect.right() { let cell = buf.get_mut(x, y); cell.set_fg(color); cell.set_symbol(tui::symbols::block::FULL); } } if fractional_progress_rect.width < bound.width { static BLOCK_SECTIONS: [&str; 9] = [ " ", tui::symbols::block::ONE_EIGHTH, tui::symbols::block::ONE_QUARTER, tui::symbols::block::THREE_EIGHTHS, tui::symbols::block::HALF, tui::symbols::block::FIVE_EIGHTHS, tui::symbols::block::THREE_QUARTERS, tui::symbols::block::SEVEN_EIGHTHS, tui::symbols::block::FULL, ]; // Get the index based on how filled the remaining part is let index = ((((bound.width as f32 * fraction) - fractional_progress_rect.width as f32) * 8f32).round() as usize) % BLOCK_SECTIONS.len(); let cell = buf.get_mut(fractional_progress_rect.right(), bound.y); cell.set_symbol(BLOCK_SECTIONS[index]); cell.set_fg(color); fractional_progress_rect.width += 1; } (fractional_progress_rect, Style::default().bg(color).fg(Color::Black)) } pub fn draw_tree(entries: &[(Key, Task)], buf: &mut Buffer, bound: Rect, offset: u16) -> u16 { let mut max_prefix_len = 0; for (line, (entry_index, entry)) in entries .iter() .enumerate() .skip(offset as usize) .take(bound.height as usize) .enumerate() { let mut line_bound = rect::line_bound(bound, line); line_bound.x = line_bound.x.saturating_sub(1); line_bound.width = line_bound.width.saturating_sub(1); let tree_prefix = format!("{} {} ", level_prefix(entries, entry_index), entry.1.name); max_prefix_len = max_prefix_len.max(block_width(&tree_prefix)); let style = if entry.1.progress.is_none() { Style::default().add_modifier(Modifier::BOLD).into() } else { None }; draw_text_with_ellipsis_nowrap(line_bound, buf, tree_prefix, style); } max_prefix_len } fn level_prefix(entries: &[(Key, Task)], entry_index: usize) -> String { let adj = Key::adjacency(entries, entry_index); let key = entries[entry_index].0; let key_level = key.level(); let is_orphan = adj.level() != key_level; let mut buf = String::with_capacity(key_level as usize); for level in 1..=key_level { use crate::progress::key::SiblingLocation::*; let is_child_level = level == key_level; if level != 1 { buf.push(' '); } if level == 1 && is_child_level { buf.push(match adj[level] { AboveAndBelow | Above => '├', NotFound | Below => '│', }); } else { let c = if is_child_level { match adj[level] { NotFound => { if is_orphan { ' ' } else { '·' } } Above => '└', Below => '┌', AboveAndBelow => '├', } } else { match adj[level] { NotFound => { if level == 1 { '│' } else if is_orphan { '·' } else { ' ' } } Above => '└', Below => '┌', AboveAndBelow => '│', } }; buf.push(c) } } buf } pub fn draw_overflow( entries: &[(Key, Task)], buf: &mut Buffer, bound: Rect, label_offset: u16, num_entries_on_display: u16, offset: u16, ) { let (count, mut progress_fraction) = entries .iter() .take(offset as usize) .chain(entries.iter().skip((offset + num_entries_on_display) as usize)) .fold((0usize, 0f32), |(count, progress_fraction), (_key, value)| { let progress = value.progress.as_ref().and_then(|p| p.fraction()).unwrap_or_default(); (count + 1, progress_fraction + progress) }); progress_fraction /= count as f32; let label = format!( "{} …{} skipped and {} more", if label_offset == 0 { "" } else { VERTICAL_LINE }, offset, entries .len() .saturating_sub((offset + num_entries_on_display + 1) as usize) ); let (progress_rect, style) = draw_progress_bar_fn(buf, bound, progress_fraction, |_| Color::Green); let bg_color = Color::Red; fill_background(rect::offset_x(bound, progress_rect.right() - 1), buf, bg_color); let color_text_according_to_progress = move |_g: &str, x: u16, _y: u16| { if x < progress_rect.right() { style } else { style.bg(bg_color) } }; draw_text_nowrap_fn( rect::offset_x(bound, label_offset), buf, label, color_text_according_to_progress, ); let help_text = "⇊ = d|↓ = j|⇈ = u|↑ = k "; draw_text_nowrap_fn( rect::snap_to_right(bound, block_width(help_text)), buf, help_text, color_text_according_to_progress, ); } prodash-28.0.0/src/render/tui/engine.rs000064400000000000000000000263411046102023000160430ustar 00000000000000use std::{ io::{self, Write}, time::Duration, }; use futures_lite::StreamExt; use tui::layout::Rect; use crate::{ render::tui::{draw, ticker}, Root, Throughput, WeakRoot, }; /// Configure the terminal user interface #[derive(Clone)] pub struct Options { /// The initial title to show for the whole window. /// /// Can be adjusted later by sending `Event::SetTitle(…)` /// into the event stream, see see [`tui::render_with_input(…events)`](./fn.render_with_input.html) function. pub title: String, /// The amount of frames to draw per second. If below 1.0, it determines the amount of seconds between the frame. /// /// *e.g.* 1.0/4.0 is one frame every 4 seconds. pub frames_per_second: f32, /// If true, (default false), we will keep track of the previous progress state to derive /// continuous throughput information from. Throughput will only show for units which have /// explicitly enabled it, it is opt-in. /// /// This comes at the cost of additional memory and CPU time. pub throughput: bool, /// If set, recompute the column width of the task tree only every given frame. Otherwise the width will be recomputed every frame. /// /// Use this if there are many short-running tasks with varying names paired with high refresh rates of multiple frames per second to /// stabilize the appearance of the TUI. /// /// For example, setting the value to 40 will with a frame rate of 20 per second will recompute the column width to fit all task names /// every 2 seconds. pub recompute_column_width_every_nth_frame: Option, /// The initial window size. /// /// If unset, it will be retrieved from the current terminal. pub window_size: Option, /// If true (default: true), we will stop running the TUI once the progress isn't available anymore (went out of scope). pub stop_if_progress_missing: bool, } impl Default for Options { fn default() -> Self { Options { title: "Progress Dashboard".into(), frames_per_second: 10.0, throughput: false, recompute_column_width_every_nth_frame: None, window_size: None, stop_if_progress_missing: true, } } } /// A line as used in [`Event::SetInformation`](./enum.Event.html#variant.SetInformation) #[derive(Debug, Clone, Eq, PartialEq)] pub enum Line { /// Set a title with the given text Title(String), /// Set a line of text with the given content Text(String), } /// The variants represented here allow the user to control when the GUI can be shutdown. #[derive(Debug, Clone, Copy)] pub enum Interrupt { /// Immediately exit the GUI event loop when there is an interrupt request. /// /// This is the default when the event loop is entered. Instantly, /// Instead of exiting the event loop instantly, wait until the next Interrupt::Instantly /// event is coming in. Deferred, } #[derive(Clone, Copy)] pub(crate) enum InterruptDrawInfo { Instantly, /// Boolean signals if interrupt is requested Deferred(bool), } #[cfg(not(any(feature = "render-tui-crossterm", feature = "render-tui-termion")))] compile_error!( "Please set either the 'render-tui-crossterm' or 'render-tui-termion' feature whne using the 'render-tui'" ); use crosstermion::crossterm::event::{KeyCode, KeyEventKind, KeyModifiers}; use crosstermion::{ input::{key_input_stream, Key}, terminal::{tui::new_terminal, AlternateRawScreen}, }; /// An event to be sent in the [`tui::render_with_input(…events)`](./fn.render_with_input.html) stream. /// /// This way, the TUI can be instructed to draw frames or change the information to be displayed. #[derive(Debug, Clone)] pub enum Event { /// Draw a frame Tick, /// Send any key - can be used to simulate user input, and is typically generated by the TUI's own input loop. Input(Key), /// Change the size of the window to the given rectangle. /// /// Useful to embed the TUI into other terminal user interfaces that can resize dynamically. SetWindowSize(Rect), /// Set the title of the progress dashboard SetTitle(String), /// Provide a list of titles and lines to populate the side bar on the right. SetInformation(Vec), /// The way the GUI will respond to interrupt requests. See `Interrupt` for more information. SetInterruptMode(Interrupt), } /// Returns a future that draws the terminal user interface indefinitely. /// /// * `progress` is the progress tree whose information to visualize. /// It will usually be changing constantly while the TUI holds it. /// * `options` are configuring the TUI. /// * `events` is a stream of `Event`s which manipulate the TUI while it is running /// /// Failure may occour if there is no terminal to draw into. pub fn render_with_input( out: impl std::io::Write, progress: impl WeakRoot, options: Options, events: impl futures_core::Stream + Send + Unpin, ) -> Result, std::io::Error> { let Options { title, frames_per_second, window_size, recompute_column_width_every_nth_frame, throughput, stop_if_progress_missing, } = options; let mut terminal = new_terminal(AlternateRawScreen::try_from(out)?)?; terminal.hide_cursor()?; let duration_per_frame = Duration::from_secs_f32(1.0 / frames_per_second); let key_receive = key_input_stream(); let render_fut = async move { let mut state = draw::State { title, duration_per_frame, ..draw::State::default() }; if throughput { state.throughput = Some(Throughput::default()); } let mut interrupt_mode = InterruptDrawInfo::Instantly; let (entries_cap, messages_cap) = progress .upgrade() .map(|p| (p.num_tasks(), p.messages_capacity())) .unwrap_or_default(); let mut entries = Vec::with_capacity(entries_cap); let mut messages = Vec::with_capacity(messages_cap); let mut events = ticker(duration_per_frame) .map(|_| Event::Tick) .or(key_receive.map(Event::Input)) .or(events); let mut tick = 0usize; let store_task_size_every = recompute_column_width_every_nth_frame.unwrap_or(1).max(1); while let Some(event) = events.next().await { let mut skip_redraw = false; match event { Event::Tick => {} Event::Input(key) if key.kind != KeyEventKind::Release => match key.code { KeyCode::Char('c') | KeyCode::Char('[') if key.modifiers.contains(KeyModifiers::CONTROL) => { match interrupt_mode { InterruptDrawInfo::Instantly => break, InterruptDrawInfo::Deferred(_) => interrupt_mode = InterruptDrawInfo::Deferred(true), } } KeyCode::Esc | KeyCode::Char('q') => match interrupt_mode { InterruptDrawInfo::Instantly => break, InterruptDrawInfo::Deferred(_) => interrupt_mode = InterruptDrawInfo::Deferred(true), }, KeyCode::Char('`') => state.hide_messages = !state.hide_messages, KeyCode::Char('~') => state.messages_fullscreen = !state.messages_fullscreen, KeyCode::Char('J') => state.message_offset = state.message_offset.saturating_add(1), KeyCode::Char('D') => state.message_offset = state.message_offset.saturating_add(10), KeyCode::Char('j') => state.task_offset = state.task_offset.saturating_add(1), KeyCode::Char('d') => state.task_offset = state.task_offset.saturating_add(10), KeyCode::Char('K') => state.message_offset = state.message_offset.saturating_sub(1), KeyCode::Char('U') => state.message_offset = state.message_offset.saturating_sub(10), KeyCode::Char('k') => state.task_offset = state.task_offset.saturating_sub(1), KeyCode::Char('u') => state.task_offset = state.task_offset.saturating_sub(10), KeyCode::Char('[') => state.hide_info = !state.hide_info, KeyCode::Char('{') => state.maximize_info = !state.maximize_info, _ => skip_redraw = true, }, Event::Input(_) => skip_redraw = true, Event::SetWindowSize(bound) => state.user_provided_window_size = Some(bound), Event::SetTitle(title) => state.title = title, Event::SetInformation(info) => state.information = info, Event::SetInterruptMode(mode) => { interrupt_mode = match mode { Interrupt::Instantly => { if let InterruptDrawInfo::Deferred(true) = interrupt_mode { break; } InterruptDrawInfo::Instantly } Interrupt::Deferred => InterruptDrawInfo::Deferred(match interrupt_mode { InterruptDrawInfo::Deferred(interrupt_requested) => interrupt_requested, _ => false, }), }; } } if !skip_redraw { tick += 1; let progress = match progress.upgrade() { Some(progress) => progress, None if stop_if_progress_missing => break, None => continue, }; progress.sorted_snapshot(&mut entries); if stop_if_progress_missing && entries.is_empty() { break; } let terminal_window_size = terminal.pre_render().expect("pre-render to work"); let window_size = state .user_provided_window_size .or(window_size) .unwrap_or(terminal_window_size); let buf = terminal.current_buffer_mut(); if !state.hide_messages { progress.copy_messages(&mut messages); } draw::all(&mut state, interrupt_mode, &entries, &messages, window_size, buf); if tick == 1 || tick % store_task_size_every == 0 || state.last_tree_column_width.unwrap_or(0) == 0 { state.next_tree_column_width = state.last_tree_column_width; } terminal.post_render().expect("post render to work"); } } // Make sure the terminal responds right away when this future stops, to reset back to the 'non-alternate' buffer drop(terminal); io::stdout().flush().ok(); }; Ok(render_fut) } /// An easy-to-use version of `render_with_input(…)` that does not allow state manipulation via an event stream. pub fn render( out: impl std::io::Write, progress: impl WeakRoot, config: Options, ) -> Result, std::io::Error> { render_with_input(out, progress, config, futures_lite::stream::pending()) } prodash-28.0.0/src/render/tui/mod.rs000064400000000000000000000036001046102023000153460ustar 00000000000000/*! * A module implementing a *terminal user interface* capable of visualizing all information stored in * [progress trees](../tree/struct.Root.html). * * **Please note** that it is behind the `render-tui` feature toggle, which is enabled by default. * * # Example * * ```should_panic * # fn main() -> Result<(), Box> { * use futures::task::{LocalSpawnExt, SpawnExt}; * use prodash::render::tui::ticker; * use prodash::Root; * // obtain a progress tree * let root = prodash::tree::Root::new(); * // Configure the gui, provide it with a handle to the ever-changing tree * let render_fut = prodash::render::tui::render( * std::io::stdout(), * root.downgrade(), * prodash::render::tui::Options { * title: "minimal example".into(), * ..Default::default() * } * )?; * // As it runs forever, we want a way to stop it. * let (render_fut, abort_handle) = futures::future::abortable(render_fut); * let pool = futures::executor::LocalPool::new(); * // Spawn the gui into the background… * let gui = pool.spawner().spawn_with_handle(async { render_fut.await.ok(); () })?; * // …and run tasks which provide progress * pool.spawner().spawn_local({ * use futures::StreamExt; * let mut progress = root.add_child("task"); * async move { * progress.init(None, None); * let mut count = 0; * let mut ticks = ticker(std::time::Duration::from_millis(100)); * while let Some(_) = ticks.next().await { * progress.set(count); * count += 1; * } * } * })?; * // …when we are done, tell the GUI to stop * abort_handle.abort(); * //…and wait until it is done * futures::executor::block_on(gui); * # Ok(()) * # } * ``` */ mod draw; mod engine; mod utils; pub use engine::*; /// Useful for bringing up the TUI without bringing in the `tui` crate yourself pub use tui as tui_export; pub use utils::ticker; prodash-28.0.0/src/render/tui/utils.rs000064400000000000000000000015551046102023000157360ustar 00000000000000use std::{future::Future, pin::Pin, task::Poll, time::Duration}; use async_io::Timer; /// Returns a stream of 'ticks', each being duration `dur` apart. /// /// Can be useful to provide the TUI with additional events in regular intervals, /// when using the [`tui::render_with_input(…events)`](./fn.render_with_input.html) function. pub fn ticker(dur: Duration) -> impl futures_core::Stream { let mut delay = Timer::after(dur); futures_lite::stream::poll_fn(move |ctx| { let res = Pin::new(&mut delay).poll(ctx); match res { Poll::Pending => Poll::Pending, Poll::Ready(_) => { delay = Timer::after(dur); Poll::Ready(Some(())) } } }) } pub const VERTICAL_LINE: &str = "│"; pub use tui_react::{draw_text_nowrap_fn, draw_text_with_ellipsis_nowrap, util::*}; prodash-28.0.0/src/throughput.rs000064400000000000000000000111001046102023000147120ustar 00000000000000use std::{ collections::VecDeque, sync::atomic::Ordering, time::{Duration, SystemTime}, }; use crate::{progress, unit}; const THROTTLE_INTERVAL: Duration = Duration::from_secs(1); const ONCE_A_SECOND: Duration = Duration::from_secs(1); #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] struct State { observed: Duration, last_value: progress::Step, elapsed_values: VecDeque<(Duration, progress::Step)>, last_update_duration: Duration, precomputed_throughput: Option, } impl State { fn new(value: progress::Step, elapsed: Duration) -> Self { State { observed: elapsed, last_value: value, elapsed_values: { let mut v = VecDeque::with_capacity(6); // default frames per second v.push_back((elapsed, value)); v }, last_update_duration: elapsed, precomputed_throughput: None, } } fn compute_throughput(&mut self) -> progress::Step { let mut observed: Duration = self.elapsed_values.iter().map(|e| e.0).sum(); while !self.elapsed_values.is_empty() && observed > ONCE_A_SECOND { let candidate = self .elapsed_values .front() .map(|e| e.0) .expect("at least one item as we are in the checked loop"); if observed.checked_sub(candidate).unwrap_or_default() <= ONCE_A_SECOND { break; } observed -= candidate; self.elapsed_values.pop_front(); } let observed_value: progress::Step = self.elapsed_values.iter().map(|e| e.1).sum(); ((observed_value as f64 / observed.as_secs_f64()) * ONCE_A_SECOND.as_secs_f64()) as progress::Step } fn update(&mut self, value: progress::Step, elapsed: Duration) -> Option { self.observed += elapsed; self.elapsed_values .push_back((elapsed, value.saturating_sub(self.last_value))); self.last_value = value; if self.observed - self.last_update_duration > THROTTLE_INTERVAL { self.precomputed_throughput = Some(self.compute_throughput()); self.last_update_duration = self.observed; } self.throughput() } fn throughput(&self) -> Option { self.precomputed_throughput.map(|tp| unit::display::Throughput { value_change_in_timespan: tp, timespan: ONCE_A_SECOND, }) } } /// A utility to compute throughput of a set of progress values usually available to a renderer. #[derive(Default)] pub struct Throughput { sorted_by_key: Vec<(progress::Key, State)>, updated_at: Option, elapsed: Option, } impl Throughput { /// Called at the beginning of the drawing of a renderer to remember at which time progress values are /// going to be updated with [`update_and_get(…)`][Throughput::update_and_get()]. pub fn update_elapsed(&mut self) { let now = SystemTime::now(); self.elapsed = self.updated_at.and_then(|then| now.duration_since(then).ok()); self.updated_at = Some(now); } /// Lookup or create the progress value at `key` and set its current `progress`, returning its computed /// throughput. pub fn update_and_get( &mut self, key: &progress::Key, progress: Option<&progress::Value>, ) -> Option { progress.and_then(|progress| { self.elapsed .and_then(|elapsed| match self.sorted_by_key.binary_search_by_key(key, |t| t.0) { Ok(index) => self.sorted_by_key[index] .1 .update(progress.step.load(Ordering::SeqCst), elapsed), Err(index) => { let state = State::new(progress.step.load(Ordering::SeqCst), elapsed); let tp = state.throughput(); self.sorted_by_key.insert(index, (*key, state)); tp } }) }) } /// Compare the keys in `sorted_values` with our internal state and remove all missing tasks from it. /// /// This should be called after [`update_and_get(…)`][Throughput::update_and_get()] to pick up removed/finished /// progress. pub fn reconcile(&mut self, sorted_values: &[(progress::Key, progress::Task)]) { self.sorted_by_key .retain(|(key, _)| sorted_values.binary_search_by_key(key, |e| e.0).is_ok()); } } prodash-28.0.0/src/time.rs000064400000000000000000000043171046102023000134530ustar 00000000000000#[cfg(feature = "local-time")] mod localtime { use std::time::SystemTime; /// Return a string representing the current date and time as localtime. /// /// Available with the `localtime` feature toggle. pub fn format_now_datetime_seconds() -> String { let t = time::OffsetDateTime::now_utc(); t.to_offset(time::UtcOffset::local_offset_at(t).unwrap_or(time::UtcOffset::UTC)) .format(&time::format_description::parse("%F %T").expect("format known to work")) .expect("formatting always works") } /// Return a string representing the current time as localtime. /// /// Available with the `localtime` feature toggle. pub fn format_time_for_messages(time: SystemTime) -> String { time::OffsetDateTime::from(time) .to_offset(time::UtcOffset::current_local_offset().unwrap_or(time::UtcOffset::UTC)) .format(&time::format_description::parse("[hour]:[minute]:[second]").expect("format known to work")) .expect("formatting always works") } } /// An `hours:minute:seconds` format. pub const DATE_TIME_HMS: usize = "00:51:45".len(); #[cfg(not(feature = "local-time"))] mod utc { use std::time::SystemTime; use super::DATE_TIME_HMS; const DATE_TIME_YMD: usize = "2020-02-13T".len(); /// Return a string representing the current date and time as UTC. /// /// Available without the `localtime` feature toggle. pub fn format_time_for_messages(time: SystemTime) -> String { String::from_utf8_lossy( &humantime::format_rfc3339_seconds(time).to_string().as_bytes() [DATE_TIME_YMD..DATE_TIME_YMD + DATE_TIME_HMS], ) .into_owned() } /// Return a string representing the current time as UTC. /// /// Available without the `localtime` feature toggle. pub fn format_now_datetime_seconds() -> String { String::from_utf8_lossy( &humantime::format_rfc3339_seconds(std::time::SystemTime::now()) .to_string() .as_bytes()[.."2020-02-13T00:51:45".len()], ) .into_owned() } } #[cfg(feature = "local-time")] pub use localtime::*; #[cfg(not(feature = "local-time"))] pub use utc::*; prodash-28.0.0/src/traits.rs000064400000000000000000000455201046102023000140240ustar 00000000000000use std::time::Instant; use crate::{messages::MessageLevel, progress, progress::Id, Unit}; /// A trait for describing hierarchical progress. pub trait NestedProgress: Progress { /// The type of progress returned by [`add_child()`][Progress::add_child()]. type SubProgress: NestedProgress; /// Adds a new child, whose parent is this instance, with the given `name`. /// /// This will make the child progress to appear contained in the parent progress. /// Note that such progress does not have a stable identifier, which can be added /// with [`add_child_with_id()`][Progress::add_child_with_id()] if desired. fn add_child(&mut self, name: impl Into) -> Self::SubProgress; /// Adds a new child, whose parent is this instance, with the given `name` and `id`. /// /// This will make the child progress to appear contained in the parent progress, and it can be identified /// using `id`. fn add_child_with_id(&mut self, name: impl Into, id: Id) -> Self::SubProgress; } /// A thread-safe read-only counter, with unknown limits. pub trait Count { /// Set the current progress to the given `step`. The cost of this call is negligible, /// making manual throttling *not* necessary. /// /// **Note**: that this call has no effect unless `init(…)` was called before. fn set(&self, step: progress::Step); /// Returns the current step, as controlled by `inc*(…)` calls fn step(&self) -> progress::Step; /// Increment the current progress to the given `step`. /// The cost of this call is negligible, making manual throttling *not* necessary. fn inc_by(&self, step: progress::Step); /// Increment the current progress to the given 1. The cost of this call is negligible, /// making manual throttling *not* necessary. fn inc(&self) { self.inc_by(1) } /// Return an atomic counter for direct access to the underlying state. /// /// This is useful if multiple threads want to access the same progress, without the need /// for provide each their own progress and aggregating the result. fn counter(&self) -> StepShared; } /// An object-safe trait for describing hierarchical progress. /// /// This will be automatically implemented for any type that implements /// [`NestedProgress`]. pub trait DynNestedProgress: Progress + impls::Sealed { /// See [`NestedProgress::add_child`] fn add_child(&mut self, name: String) -> BoxedDynNestedProgress; /// See [`NestedProgress::add_child_with_id`] fn add_child_with_id(&mut self, name: String, id: Id) -> BoxedDynNestedProgress; } /// An opaque type for storing [`DynNestedProgress`]. pub struct BoxedDynNestedProgress(Box); /// An owned version of [`Progress`] which can itself implement said trait. pub type BoxedProgress = Box; /// A bridge type that implements [`NestedProgress`] for any type that implements [`DynNestedProgress`]. pub struct DynNestedProgressToNestedProgress(pub T); /// A trait for describing non-hierarchical progress. /// /// It differs by not being able to add child progress dynamically, but in turn is object safe. It's recommended to /// use this trait whenever there is no need to add child progress, at the leaf of a computation. // NOTE: keep this in-sync with `Progress`. pub trait Progress: Count + Send + Sync { /// Initialize the Item for receiving progress information. /// /// If `max` is `Some(…)`, it will be treated as upper bound. When progress is [set(…)](./struct.Item.html#method.set) /// it should not exceed the given maximum. /// If `max` is `None`, the progress is unbounded. Use this if the amount of work cannot accurately /// be determined in advance. /// /// If `unit` is `Some(…)`, it is used for display purposes only. See `prodash::Unit` for more information. /// /// If both `unit` and `max` are `None`, the item will be reset to be equivalent to 'uninitialized'. /// /// If this method is never called, this `Progress` instance will serve as organizational unit, useful to add more structure /// to the progress tree (e.g. a headline). /// /// **Note** that this method can be called multiple times, changing the bounded-ness and unit at will. fn init(&mut self, max: Option, unit: Option); /// Returns the (cloned) unit associated with this Progress fn unit(&self) -> Option { None } /// Returns the maximum about of items we expect, as provided with the `init(…)` call fn max(&self) -> Option { None } /// Set the maximum value to `max` and return the old maximum value. fn set_max(&mut self, _max: Option) -> Option { None } /// Set the name of the instance, altering the value given when crating it with `add_child(…)` /// The progress is allowed to discard it. fn set_name(&mut self, name: String); /// Get the name of the instance as given when creating it with `add_child(…)` /// The progress is allowed to not be named, thus there is no guarantee that a previously set names 'sticks'. fn name(&self) -> Option; /// Get a stable identifier for the progress instance. /// Note that it could be [unknown][crate::progress::UNKNOWN]. fn id(&self) -> Id; /// Create a `message` of the given `level` and store it with the progress tree. /// /// Use this to provide additional,human-readable information about the progress /// made, including indicating success or failure. fn message(&self, level: MessageLevel, message: String); /// Create a message providing additional information about the progress thus far. fn info(&self, message: String) { self.message(MessageLevel::Info, message) } /// Create a message indicating the task is done successfully fn done(&self, message: String) { self.message(MessageLevel::Success, message) } /// Create a message indicating the task failed fn fail(&self, message: String) { self.message(MessageLevel::Failure, message) } /// A shorthand to print throughput information fn show_throughput(&self, start: Instant) { let step = self.step(); match self.unit() { Some(unit) => self.show_throughput_with(start, step, unit, MessageLevel::Info), None => { let elapsed = start.elapsed().as_secs_f32(); let steps_per_second = (step as f32 / elapsed) as progress::Step; self.info(format!( "done {} items in {:.02}s ({} items/s)", step, elapsed, steps_per_second )) } }; } /// A shorthand to print throughput information, with the given step and unit, and message level. fn show_throughput_with(&self, start: Instant, step: progress::Step, unit: Unit, level: MessageLevel) { use std::fmt::Write; let elapsed = start.elapsed().as_secs_f32(); let steps_per_second = (step as f32 / elapsed) as progress::Step; let mut buf = String::with_capacity(128); let unit = unit.as_display_value(); let push_unit = |buf: &mut String| { buf.push(' '); let len_before_unit = buf.len(); unit.display_unit(buf, step).ok(); if buf.len() == len_before_unit { buf.pop(); } }; buf.push_str("done "); unit.display_current_value(&mut buf, step, None).ok(); push_unit(&mut buf); buf.write_fmt(format_args!(" in {:.02}s (", elapsed)).ok(); unit.display_current_value(&mut buf, steps_per_second, None).ok(); push_unit(&mut buf); buf.push_str("/s)"); self.message(level, buf); } } use crate::{ messages::{Message, MessageCopyState}, progress::StepShared, }; /// The top-level root as weak handle, which needs an upgrade to become a usable root. /// /// If the underlying reference isn't present anymore, such upgrade will fail permanently. pub trait WeakRoot { /// The type implementing the `Root` trait type Root: Root; /// Equivalent to `std::sync::Weak::upgrade()`. fn upgrade(&self) -> Option; } /// The top level of a progress task hierarchy, with `progress::Task`s identified with `progress::Key`s pub trait Root { /// The type implementing the `WeakRoot` trait type WeakRoot: WeakRoot; /// Returns the maximum amount of messages we can keep before overwriting older ones. fn messages_capacity(&self) -> usize; /// Returns the current amount of tasks underneath the root, transitively. /// **Note** that this is at most a guess as tasks can be added and removed in parallel. fn num_tasks(&self) -> usize; /// Copy the entire progress tree into the given `out` vector, so that /// it can be traversed from beginning to end in order of hierarchy. /// The `out` vec will be cleared automatically. fn sorted_snapshot(&self, out: &mut Vec<(progress::Key, progress::Task)>); /// Copy all messages from the internal ring buffer into the given `out` /// vector. Messages are ordered from oldest to newest. fn copy_messages(&self, out: &mut Vec); /// Copy only new messages from the internal ring buffer into the given `out` /// vector. Messages are ordered from oldest to newest. fn copy_new_messages(&self, out: &mut Vec, prev: Option) -> MessageCopyState; /// Similar to `Arc::downgrade()` fn downgrade(&self) -> Self::WeakRoot; } mod impls { use std::{ ops::{Deref, DerefMut}, time::Instant, }; use crate::traits::{BoxedProgress, Progress}; use crate::{ messages::MessageLevel, progress::{Id, Step, StepShared}, BoxedDynNestedProgress, Count, DynNestedProgress, DynNestedProgressToNestedProgress, NestedProgress, Unit, }; pub trait Sealed {} impl<'a, T> Count for &'a T where T: Count + ?Sized, { fn set(&self, step: Step) { (*self).set(step) } fn step(&self) -> Step { (*self).step() } fn inc_by(&self, step: Step) { (*self).inc_by(step) } fn inc(&self) { (*self).inc() } fn counter(&self) -> StepShared { (*self).counter() } } impl<'a, T> Count for &'a mut T where T: Count + ?Sized, { fn set(&self, step: Step) { self.deref().set(step) } fn step(&self) -> Step { self.deref().step() } fn inc_by(&self, step: Step) { self.deref().inc_by(step) } fn inc(&self) { self.deref().inc() } fn counter(&self) -> StepShared { self.deref().counter() } } impl<'a, T> Progress for &'a mut T where T: Progress + ?Sized, { fn init(&mut self, max: Option, unit: Option) { self.deref_mut().init(max, unit) } fn unit(&self) -> Option { self.deref().unit() } fn max(&self) -> Option { self.deref().max() } fn set_max(&mut self, max: Option) -> Option { self.deref_mut().set_max(max) } fn set_name(&mut self, name: String) { self.deref_mut().set_name(name) } fn name(&self) -> Option { self.deref().name() } fn id(&self) -> Id { self.deref().id() } fn message(&self, level: MessageLevel, message: String) { self.deref().message(level, message) } fn info(&self, message: String) { self.deref().info(message) } fn done(&self, message: String) { self.deref().done(message) } fn fail(&self, message: String) { self.deref().fail(message) } fn show_throughput(&self, start: Instant) { self.deref().show_throughput(start) } fn show_throughput_with(&self, start: Instant, step: Step, unit: Unit, level: MessageLevel) { self.deref().show_throughput_with(start, step, unit, level) } } impl<'a, T> NestedProgress for &'a mut T where T: NestedProgress + ?Sized, { type SubProgress = T::SubProgress; fn add_child(&mut self, name: impl Into) -> Self::SubProgress { self.deref_mut().add_child(name) } fn add_child_with_id(&mut self, name: impl Into, id: Id) -> Self::SubProgress { self.deref_mut().add_child_with_id(name, id) } } impl Sealed for T where T: NestedProgress + ?Sized {} impl DynNestedProgress for T where T: NestedProgress + ?Sized, SubP: NestedProgress + 'static, { fn add_child(&mut self, name: String) -> BoxedDynNestedProgress { BoxedDynNestedProgress::new(self.add_child(name)) } fn add_child_with_id(&mut self, name: String, id: Id) -> BoxedDynNestedProgress { BoxedDynNestedProgress::new(self.add_child_with_id(name, id)) } } impl BoxedDynNestedProgress { /// Create new instance from a `DynProgress` implementation. pub fn new(progress: impl DynNestedProgress + 'static) -> Self { Self(Box::new(progress)) } } impl Progress for BoxedDynNestedProgress { fn init(&mut self, max: Option, unit: Option) { self.0.init(max, unit) } fn unit(&self) -> Option { self.0.unit() } fn max(&self) -> Option { self.0.max() } fn set_max(&mut self, max: Option) -> Option { self.0.set_max(max) } fn set_name(&mut self, name: String) { self.0.set_name(name) } fn name(&self) -> Option { self.0.name() } fn id(&self) -> Id { self.0.id() } fn message(&self, level: MessageLevel, message: String) { self.0.message(level, message) } fn show_throughput(&self, start: Instant) { self.0.show_throughput(start) } fn show_throughput_with(&self, start: Instant, step: Step, unit: Unit, level: MessageLevel) { self.0.show_throughput_with(start, step, unit, level) } } impl Progress for BoxedProgress { fn init(&mut self, max: Option, unit: Option) { self.deref_mut().init(max, unit) } fn unit(&self) -> Option { self.deref().unit() } fn max(&self) -> Option { self.deref().max() } fn set_max(&mut self, max: Option) -> Option { self.deref_mut().set_max(max) } fn set_name(&mut self, name: String) { self.deref_mut().set_name(name) } fn name(&self) -> Option { self.deref().name() } fn id(&self) -> Id { self.deref().id() } fn message(&self, level: MessageLevel, message: String) { self.deref().message(level, message) } fn show_throughput(&self, start: Instant) { self.deref().show_throughput(start) } fn show_throughput_with(&self, start: Instant, step: Step, unit: Unit, level: MessageLevel) { self.deref().show_throughput_with(start, step, unit, level) } } impl Count for BoxedDynNestedProgress { fn set(&self, step: Step) { self.0.set(step) } fn step(&self) -> Step { self.0.step() } fn inc_by(&self, step: Step) { self.0.inc_by(step) } fn inc(&self) { self.0.inc() } fn counter(&self) -> StepShared { self.0.counter() } } impl Count for BoxedProgress { fn set(&self, step: Step) { self.deref().set(step) } fn step(&self) -> Step { self.deref().step() } fn inc_by(&self, step: Step) { self.deref().inc_by(step) } fn inc(&self) { self.deref().inc() } fn counter(&self) -> StepShared { self.deref().counter() } } impl NestedProgress for BoxedDynNestedProgress { type SubProgress = Self; fn add_child(&mut self, name: impl Into) -> Self::SubProgress { self.0.add_child(name.into()) } fn add_child_with_id(&mut self, name: impl Into, id: Id) -> Self::SubProgress { self.0.add_child_with_id(name.into(), id) } } impl Progress for DynNestedProgressToNestedProgress where T: ?Sized + Progress, { fn init(&mut self, max: Option, unit: Option) { self.0.init(max, unit) } fn unit(&self) -> Option { self.0.unit() } fn max(&self) -> Option { self.0.max() } fn set_max(&mut self, max: Option) -> Option { self.0.set_max(max) } fn set_name(&mut self, name: String) { self.0.set_name(name) } fn name(&self) -> Option { self.0.name() } fn id(&self) -> Id { self.0.id() } fn message(&self, level: MessageLevel, message: String) { self.0.message(level, message) } fn show_throughput(&self, start: Instant) { self.0.show_throughput(start) } fn show_throughput_with(&self, start: Instant, step: Step, unit: Unit, level: MessageLevel) { self.0.show_throughput_with(start, step, unit, level) } } impl Count for DynNestedProgressToNestedProgress where T: ?Sized + Count, { fn set(&self, step: Step) { self.0.set(step) } fn step(&self) -> Step { self.0.step() } fn inc_by(&self, step: Step) { self.0.inc_by(step) } fn inc(&self) { self.0.inc() } fn counter(&self) -> StepShared { self.0.counter() } } impl NestedProgress for DynNestedProgressToNestedProgress where T: DynNestedProgress + ?Sized, { type SubProgress = BoxedDynNestedProgress; fn add_child(&mut self, name: impl Into) -> Self::SubProgress { self.0.add_child(name.into()) } fn add_child_with_id(&mut self, name: impl Into, id: Id) -> Self::SubProgress { self.0.add_child_with_id(name.into(), id) } } } prodash-28.0.0/src/tree/item.rs000064400000000000000000000334211046102023000144100ustar 00000000000000use std::{ fmt::Debug, ops::Deref, sync::{ atomic::{AtomicUsize, Ordering}, Arc, }, time::SystemTime, }; use parking_lot::Mutex; use crate::{ messages::MessageLevel, progress::{Id, State, Step, StepShared, Task, Value}, tree::Item, unit::Unit, }; impl Drop for Item { fn drop(&mut self) { self.tree.remove(&self.key); } } impl Debug for Item { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("Item") .field("key", &self.key) .field("value", &self.value) .finish_non_exhaustive() } } impl Item { /// Initialize the Item for receiving progress information. /// /// If `max` is `Some(…)`, it will be treated as upper bound. When progress is [set(…)](./struct.Item.html#method.set) /// it should not exceed the given maximum. /// If `max` is `None`, the progress is unbounded. Use this if the amount of work cannot accurately /// be determined. /// /// If `unit` is `Some(…)`, it is used for display purposes only. It should be using the plural. /// /// If this method is never called, this `Item` will serve as organizational unit, useful to add more structure /// to the progress tree. /// /// **Note** that this method can be called multiple times, changing the bounded-ness and unit at will. pub fn init(&self, max: Option, unit: Option) { #[cfg(feature = "progress-tree-hp-hashmap")] { if let Some(mut r) = self.tree.get_mut(&self.key) { self.value.store(0, Ordering::SeqCst); r.value_mut().progress = (max.is_some() || unit.is_some()).then(|| Value { done_at: max, unit, step: Arc::clone(&self.value), ..Default::default() }) }; } #[cfg(not(feature = "progress-tree-hp-hashmap"))] { self.tree.get_mut(&self.key, |v| { self.value.store(0, Ordering::SeqCst); v.progress = (max.is_some() || unit.is_some()).then(|| Value { done_at: max, unit, step: Arc::clone(&self.value), ..Default::default() }); }); } } fn alter_progress(&self, f: impl FnMut(&mut Value)) { #[cfg(feature = "progress-tree-hp-hashmap")] { if let Some(mut r) = self.tree.get_mut(&self.key) { // NOTE: since we wrap around, if there are more tasks than we can have IDs for, // and if all these tasks are still alive, two progress trees may see the same ID // when these go out of scope, they delete the key and the other tree will not find // its value anymore. Besides, it's probably weird to see tasks changing their progress // all the time… r.value_mut().progress.as_mut().map(f); }; } #[cfg(not(feature = "progress-tree-hp-hashmap"))] { self.tree.get_mut(&self.key, |v| { v.progress.as_mut().map(f); }); } } /// Set the name of this task's progress to the given `name`. pub fn set_name(&self, name: impl Into) { #[cfg(feature = "progress-tree-hp-hashmap")] { if let Some(mut r) = self.tree.get_mut(&self.key) { r.value_mut().name = name.into(); }; } #[cfg(not(feature = "progress-tree-hp-hashmap"))] { self.tree.get_mut(&self.key, |v| { v.name = name.into(); }); } } /// Get the name of this task's progress pub fn name(&self) -> Option { #[cfg(feature = "progress-tree-hp-hashmap")] { self.tree.get(&self.key).map(|r| r.value().name.to_owned()) } #[cfg(not(feature = "progress-tree-hp-hashmap"))] { self.tree.get(&self.key, |v| v.name.to_owned()) } } /// Get the stable identifier of this instance. pub fn id(&self) -> Id { #[cfg(feature = "progress-tree-hp-hashmap")] { self.tree .get(&self.key) .map(|r| r.value().id) .unwrap_or(crate::progress::UNKNOWN) } #[cfg(not(feature = "progress-tree-hp-hashmap"))] { self.tree.get(&self.key, |v| v.id).unwrap_or(crate::progress::UNKNOWN) } } /// Returns the current step, as controlled by `inc*(…)` calls pub fn step(&self) -> Option { self.value.load(Ordering::Relaxed).into() } /// Returns the maximum about of items we expect, as provided with the `init(…)` call pub fn max(&self) -> Option { #[cfg(feature = "progress-tree-hp-hashmap")] { self.tree .get(&self.key) .and_then(|r| r.value().progress.as_ref().and_then(|p| p.done_at)) } #[cfg(not(feature = "progress-tree-hp-hashmap"))] { self.tree .get(&self.key, |v| v.progress.as_ref().and_then(|p| p.done_at)) .flatten() } } /// Set the maximum value to `max` and return the old maximum value. pub fn set_max(&self, max: Option) -> Option { #[cfg(feature = "progress-tree-hp-hashmap")] { self.tree .get_mut(&self.key)? .value_mut() .progress .as_mut() .and_then(|p| { let prev = p.done_at; p.done_at = max; prev }) } #[cfg(not(feature = "progress-tree-hp-hashmap"))] { self.tree .get_mut(&self.key, |v| { v.progress.as_mut().and_then(|p| { let prev = p.done_at; p.done_at = max; prev }) }) .flatten() } } /// Returns the (cloned) unit associated with this Progress pub fn unit(&self) -> Option { #[cfg(feature = "progress-tree-hp-hashmap")] { self.tree .get(&self.key) .and_then(|r| r.value().progress.as_ref().and_then(|p| p.unit.clone())) } #[cfg(not(feature = "progress-tree-hp-hashmap"))] { self.tree .get(&self.key, |v| v.progress.as_ref().and_then(|p| p.unit.clone())) .flatten() } } /// Set the current progress to the given `step`. /// /// **Note**: that this call has no effect unless `init(…)` was called before. pub fn set(&self, step: Step) { self.value.store(step, Ordering::SeqCst); } /// Increment the current progress by the given `step`. /// /// **Note**: that this call has no effect unless `init(…)` was called before. pub fn inc_by(&self, step: Step) { self.value.fetch_add(step, Ordering::Relaxed); } /// Increment the current progress by one. /// /// **Note**: that this call has no effect unless `init(…)` was called before. pub fn inc(&self) { self.value.fetch_add(1, Ordering::Relaxed); } /// Call to indicate that progress cannot be indicated, and that the task cannot be interrupted. /// Use this, as opposed to `halted(…)`, if a non-interruptable call is about to be made without support /// for any progress indication. /// /// If `eta` is `Some(…)`, it specifies the time at which this task is expected to /// make progress again. /// /// The halted-state is undone next time [`tree::Item::running(…)`][Item::running()] is called. pub fn blocked(&self, reason: &'static str, eta: Option) { self.alter_progress(|p| p.state = State::Blocked(reason, eta)); } /// Call to indicate that progress cannot be indicated, even though the task can be interrupted. /// Use this, as opposed to `blocked(…)`, if an interruptable call is about to be made without support /// for any progress indication. /// /// If `eta` is `Some(…)`, it specifies the time at which this task is expected to /// make progress again. /// /// The halted-state is undone next time [`tree::Item::running(…)`][Item::running()] is called. pub fn halted(&self, reason: &'static str, eta: Option) { self.alter_progress(|p| p.state = State::Halted(reason, eta)); } /// Call to indicate that progress is back in running state, which should be called after the reason for /// calling `blocked()` or `halted()` has passed. pub fn running(&self) { self.alter_progress(|p| p.state = State::Running); } /// Adds a new child `Tree`, whose parent is this instance, with the given `name`. /// /// **Important**: The depth of the hierarchy is limited to [`tree::Key::max_level`](./struct.Key.html#method.max_level). /// Exceeding the level will be ignored, and new tasks will be added to this instance's /// level instead. pub fn add_child(&mut self, name: impl Into) -> Item { self.add_child_with_id(name, crate::progress::UNKNOWN) } /// Adds a new child `Tree`, whose parent is this instance, with the given `name` and `id`. /// /// **Important**: The depth of the hierarchy is limited to [`tree::Key::max_level`](./struct.Key.html#method.max_level). /// Exceeding the level will be ignored, and new tasks will be added to this instance's /// level instead. pub fn add_child_with_id(&mut self, name: impl Into, id: Id) -> Item { let child_key = self.key.add_child(self.highest_child_id); let task = Task { name: name.into(), id, progress: None, }; #[cfg(feature = "progress-tree-hp-hashmap")] self.tree.insert(child_key, task); #[cfg(not(feature = "progress-tree-hp-hashmap"))] self.tree.insert(child_key, task); self.highest_child_id = self.highest_child_id.wrapping_add(1); Item { highest_child_id: 0, value: Default::default(), key: child_key, tree: Arc::clone(&self.tree), messages: Arc::clone(&self.messages), } } /// Create a `message` of the given `level` and store it with the progress tree. /// /// Use this to provide additional,human-readable information about the progress /// made, including indicating success or failure. pub fn message(&self, level: MessageLevel, message: impl Into) { let message: String = message.into(); self.messages.lock().push_overwrite( level, { let name; #[cfg(feature = "progress-tree-hp-hashmap")] { name = self.tree.get(&self.key).map(|v| v.name.to_owned()).unwrap_or_default(); } #[cfg(not(feature = "progress-tree-hp-hashmap"))] { name = self.tree.get(&self.key, |v| v.name.to_owned()).unwrap_or_default() } #[cfg(feature = "progress-tree-log")] match level { MessageLevel::Failure => crate::warn!("{} → {}", name, message), MessageLevel::Info | MessageLevel::Success => crate::info!("{} → {}", name, message), }; name }, message, ) } /// Create a message indicating the task is done pub fn done(&mut self, message: impl Into) { self.message(MessageLevel::Success, message) } /// Create a message indicating the task failed pub fn fail(&mut self, message: impl Into) { self.message(MessageLevel::Failure, message) } /// Create a message providing additional information about the progress thus far. pub fn info(&mut self, message: impl Into) { self.message(MessageLevel::Info, message) } pub(crate) fn deep_clone(&self) -> Item { Item { key: self.key, value: Arc::new(AtomicUsize::new(self.value.load(Ordering::SeqCst))), highest_child_id: self.highest_child_id, tree: Arc::new(self.tree.deref().clone()), messages: Arc::new(Mutex::new(self.messages.lock().clone())), } } } impl crate::Count for Item { fn set(&self, step: usize) { Item::set(self, step) } fn step(&self) -> usize { Item::step(self).unwrap_or(0) } fn inc_by(&self, step: usize) { self.inc_by(step) } fn counter(&self) -> StepShared { Arc::clone(&self.value) } } impl crate::Progress for Item { fn init(&mut self, max: Option, unit: Option) { Item::init(self, max, unit) } fn unit(&self) -> Option { Item::unit(self) } fn max(&self) -> Option { Item::max(self) } fn set_max(&mut self, max: Option) -> Option { Item::set_max(self, max) } fn set_name(&mut self, name: String) { Item::set_name(self, name) } fn name(&self) -> Option { Item::name(self) } fn id(&self) -> Id { Item::id(self) } fn message(&self, level: MessageLevel, message: String) { Item::message(self, level, message) } } impl crate::NestedProgress for Item { type SubProgress = Item; fn add_child(&mut self, name: impl Into) -> Self { Item::add_child(self, name) } fn add_child_with_id(&mut self, name: impl Into, id: Id) -> Self { Item::add_child_with_id(self, name, id) } } prodash-28.0.0/src/tree/mod.rs000064400000000000000000000051131046102023000142260ustar 00000000000000use crate::messages::MessageRingBuffer; /// The top-level of the progress tree. #[derive(Debug)] pub struct Root { pub(crate) inner: parking_lot::Mutex, } /// A `Tree` represents an element of the progress tree. /// /// It can be used to set progress and send messages. /// ```rust /// let tree = prodash::tree::Root::new(); /// let mut progress = tree.add_child("task 1"); /// /// progress.init(Some(10), Some("elements".into())); /// for p in 0..10 { /// progress.set(p); /// } /// progress.done("great success"); /// let mut sub_progress = progress.add_child_with_id("sub-task 1", *b"TSK2"); /// sub_progress.init(None, None); /// sub_progress.set(5); /// sub_progress.fail("couldn't finish"); /// ``` pub struct Item { pub(crate) key: crate::progress::Key, pub(crate) value: crate::progress::StepShared, pub(crate) highest_child_id: crate::progress::key::Id, pub(crate) tree: std::sync::Arc>, pub(crate) messages: std::sync::Arc>, } #[cfg(feature = "dashmap")] type HashMap = dashmap::DashMap; #[cfg(not(feature = "dashmap"))] type HashMap = sync::HashMap; #[cfg(not(feature = "dashmap"))] pub(crate) mod sync { pub struct HashMap(parking_lot::Mutex>); impl HashMap where K: Eq + std::hash::Hash, { pub fn with_capacity(cap: usize) -> Self { HashMap(parking_lot::Mutex::new(std::collections::HashMap::with_capacity(cap))) } pub fn extend_to(&self, out: &mut Vec<(K, V)>) where K: Clone, V: Clone, { let lock = self.0.lock(); out.extend(lock.iter().map(|(k, v)| (k.clone(), v.clone()))) } pub fn remove(&self, key: &K) -> Option { self.0.lock().remove(key) } pub fn get(&self, key: &K, cb: impl FnOnce(&V) -> T) -> Option { self.0.lock().get(key).map(cb) } pub fn get_mut(&self, key: &K, cb: impl FnOnce(&mut V) -> T) -> Option { self.0.lock().get_mut(key).map(cb) } pub fn insert(&self, key: K, value: V) { self.0.lock().insert(key, value); } pub fn len(&self) -> usize { self.0.lock().len() } pub fn clone(&self) -> Self where K: Clone, V: Clone, { HashMap(parking_lot::Mutex::new(self.0.lock().clone())) } } } mod item; /// pub mod root; #[cfg(test)] mod tests; prodash-28.0.0/src/tree/root.rs000064400000000000000000000132121046102023000144310ustar 00000000000000use std::{ ops::Deref, sync::{atomic::AtomicUsize, Arc, Weak}, }; use parking_lot::Mutex; use crate::{ messages::{Message, MessageCopyState, MessageRingBuffer}, progress::{Id, Key, Task}, tree::{Item, Root}, }; impl Root { /// Create a new tree with default configuration. /// /// As opposed to [Item](./struct.Item.html) instances, this type can be closed and sent /// safely across threads. pub fn new() -> Arc { Options::default().into() } /// Returns the maximum amount of messages we can keep before overwriting older ones. pub fn messages_capacity(&self) -> usize { self.inner.lock().messages.lock().buf.capacity() } /// Returns the current amount of `Item`s stored in the tree. /// **Note** that this is at most a guess as tasks can be added and removed in parallel. pub fn num_tasks(&self) -> usize { #[cfg(feature = "progress-tree-hp-hashmap")] { self.inner.lock().tree.len() } #[cfg(not(feature = "progress-tree-hp-hashmap"))] { self.inner.lock().tree.len() } } /// Adds a new child `tree::Item`, whose parent is this instance, with the given `name`. /// /// This builds a hierarchy of `tree::Item`s, each having their own progress. /// Use this method to [track progress](./struct.Item.html) of your first tasks. pub fn add_child(&self, name: impl Into) -> Item { self.inner.lock().add_child(name) } /// Adds a new child `tree::Item`, whose parent is this instance, with the given `name` and `id`. /// /// This builds a hierarchy of `tree::Item`s, each having their own progress. /// Use this method to [track progress](./struct.Item.html) of your first tasks. pub fn add_child_with_id(&self, name: impl Into, id: Id) -> Item { self.inner.lock().add_child_with_id(name, id) } /// Copy the entire progress tree into the given `out` vector, so that /// it can be traversed from beginning to end in order of hierarchy. pub fn sorted_snapshot(&self, out: &mut Vec<(Key, Task)>) { out.clear(); #[cfg(feature = "progress-tree-hp-hashmap")] out.extend(self.inner.lock().tree.iter().map(|r| (*r.key(), r.value().clone()))); #[cfg(not(feature = "progress-tree-hp-hashmap"))] self.inner.lock().tree.extend_to(out); out.sort_by_key(|t| t.0); } /// Copy all messages from the internal ring buffer into the given `out` /// vector. Messages are ordered from oldest to newest. pub fn copy_messages(&self, out: &mut Vec) { self.inner.lock().messages.lock().copy_all(out); } /// Copy only new messages from the internal ring buffer into the given `out` /// vector. Messages are ordered from oldest to newest. pub fn copy_new_messages(&self, out: &mut Vec, prev: Option) -> MessageCopyState { self.inner.lock().messages.lock().copy_new(out, prev) } /// Duplicate all content and return it. /// /// This is an expensive operation, whereas `clone()` is not as it is shallow. pub fn deep_clone(&self) -> Arc { Arc::new(Root { inner: Mutex::new(self.inner.lock().deep_clone()), }) } } /// A way to configure new [`tree::Root`](./tree/struct.Root.html) instances /// ```rust /// let tree = prodash::tree::root::Options::default().create(); /// let tree2 = prodash::tree::root::Options { message_buffer_capacity: 100, ..Default::default() }.create(); /// ``` #[derive(Clone, Debug)] pub struct Options { /// The amount of [items][Item] the tree can hold without being forced to allocate. pub initial_capacity: usize, /// The amount of messages we can hold before we start overwriting old ones. pub message_buffer_capacity: usize, } impl Options { /// Create a new [`Root`](./tree/struct.Root.html) instance from the /// configuration within. pub fn create(self) -> Root { self.into() } } impl Default for Options { fn default() -> Self { Options { initial_capacity: 100, message_buffer_capacity: 20, } } } impl From for Arc { fn from(opts: Options) -> Self { Arc::new(opts.into()) } } impl From for Root { fn from( Options { initial_capacity, message_buffer_capacity, }: Options, ) -> Self { Root { inner: Mutex::new(Item { highest_child_id: 0, value: Arc::new(AtomicUsize::default()), key: Key::default(), tree: Arc::new(crate::tree::HashMap::with_capacity(initial_capacity)), messages: Arc::new(Mutex::new(MessageRingBuffer::with_capacity(message_buffer_capacity))), }), } } } impl crate::WeakRoot for Weak { type Root = Arc; fn upgrade(&self) -> Option { Weak::upgrade(self) } } impl crate::Root for Arc { type WeakRoot = Weak; fn messages_capacity(&self) -> usize { self.deref().messages_capacity() } fn num_tasks(&self) -> usize { self.deref().num_tasks() } fn sorted_snapshot(&self, out: &mut Vec<(Key, Task)>) { self.deref().sorted_snapshot(out) } fn copy_messages(&self, out: &mut Vec) { self.deref().copy_messages(out) } fn copy_new_messages(&self, out: &mut Vec, prev: Option) -> MessageCopyState { self.deref().copy_new_messages(out, prev) } fn downgrade(&self) -> Self::WeakRoot { Arc::downgrade(self) } } prodash-28.0.0/src/tree/tests.rs000064400000000000000000000076561046102023000146270ustar 00000000000000mod message_buffer { use crate::messages::{Message, MessageLevel, MessageRingBuffer}; fn push(buf: &mut MessageRingBuffer, msg: impl Into) { buf.push_overwrite(MessageLevel::Info, "test".into(), msg); } fn push_and_copy_all(buf: &mut MessageRingBuffer, msg: impl Into, out: &mut Vec) { push(buf, msg); buf.copy_all(out); } fn assert_messages(actual: &[Message], expected: &[&'static str]) { let actual: Vec<_> = actual.iter().map(|m| m.message.as_str()).collect(); assert_eq!(expected, actual.as_slice(), "messages are ordered old to new"); } #[test] fn copy_all() { let mut buf = MessageRingBuffer::with_capacity(2); let mut out = Vec::new(); buf.copy_all(&mut out); assert_eq!(out, buf.buf); push_and_copy_all(&mut buf, "one", &mut out); assert_eq!(out, buf.buf); push_and_copy_all(&mut buf, "two", &mut out); assert_eq!(out, buf.buf); push_and_copy_all(&mut buf, "three", &mut out); assert_messages(&out, &["two", "three"]); push_and_copy_all(&mut buf, "four", &mut out); assert_messages(&out, &["three", "four"]); push_and_copy_all(&mut buf, "five", &mut out); buf.copy_all(&mut out); assert_messages(&out, &["four", "five"]); } mod copy_new { use crate::{ messages::{Message, MessageCopyState, MessageRingBuffer}, tree::tests::message_buffer::{assert_messages, push}, }; #[test] fn without_state() { fn push_and_copy_new(buf: &mut MessageRingBuffer, msg: impl Into, out: &mut Vec) { push(buf, msg); buf.copy_new(out, None); } let mut buf = MessageRingBuffer::with_capacity(2); let mut out = Vec::new(); buf.copy_new(&mut out, None); assert_eq!(out, buf.buf); push_and_copy_new(&mut buf, "one", &mut out); assert_eq!(out, buf.buf); push_and_copy_new(&mut buf, "two", &mut out); assert_eq!(out, buf.buf); push_and_copy_new(&mut buf, "three", &mut out); assert_messages(&out, &["two", "three"]); } #[test] fn with_continous_state() { fn push_and_copy_new( buf: &mut MessageRingBuffer, msg: impl Into, out: &mut Vec, state: Option, ) -> Option { push(buf, msg); Some(buf.copy_new(out, state)) } let mut buf = MessageRingBuffer::with_capacity(2); let mut out = Vec::new(); let mut state = push_and_copy_new(&mut buf, "one", &mut out, None); assert_eq!(out, buf.buf); state = push_and_copy_new(&mut buf, "two", &mut out, state); assert_messages(&out, &["two"]); state = push_and_copy_new(&mut buf, "three", &mut out, state); assert_messages(&out, &["three"]); state = push_and_copy_new(&mut buf, "four", &mut out, state); assert_messages(&out, &["four"]); push_and_copy_new(&mut buf, "five", &mut out, state); assert_messages(&out, &["five"]); state = push_and_copy_new(&mut buf, "six", &mut out, None); assert_messages(&out, &["five", "six"]); state = Some(buf.copy_new(&mut out, state)); assert_messages(&out, &[]); push(&mut buf, "seven"); push(&mut buf, "eight"); state = Some(buf.copy_new(&mut out, state)); assert_messages(&out, &["seven", "eight"]); push(&mut buf, "1"); push(&mut buf, "2"); push(&mut buf, "3"); buf.copy_new(&mut out, state); assert_messages(&out, &["2", "3"]); } } } prodash-28.0.0/src/unit/bytes.rs000064400000000000000000000017651046102023000146260ustar 00000000000000use std::fmt; use crate::{progress::Step, unit::DisplayValue}; /// A marker for formatting numbers as bytes in renderers. #[derive(Copy, Clone, Default, Eq, PartialEq, Ord, PartialOrd, Debug)] pub struct Bytes; impl Bytes { fn format_bytes(w: &mut dyn fmt::Write, value: Step) -> fmt::Result { let string = bytesize::to_string(value as u64, false); for token in string.split(' ') { w.write_str(token)?; } Ok(()) } } impl DisplayValue for Bytes { fn display_current_value(&self, w: &mut dyn fmt::Write, value: Step, _upper: Option) -> fmt::Result { Self::format_bytes(w, value) } fn display_upper_bound(&self, w: &mut dyn fmt::Write, upper_bound: Step, _value: Step) -> fmt::Result { Self::format_bytes(w, upper_bound) } fn dyn_hash(&self, state: &mut dyn std::hash::Hasher) { state.write(&[]) } fn display_unit(&self, _w: &mut dyn fmt::Write, _value: Step) -> fmt::Result { Ok(()) } } prodash-28.0.0/src/unit/display.rs000064400000000000000000000131621046102023000151370ustar 00000000000000use std::fmt::{self, Write}; use crate::{ progress::Step, unit::{DisplayValue, Unit}, }; /// The location at which [`Throughput`] or [`UnitDisplays`][UnitDisplay] should be placed. #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)] #[allow(missing_docs)] pub enum Location { BeforeValue, AfterUnit, } /// A structure able to display throughput, a value change within a given duration. #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] pub struct Throughput { /// The change of value between the current value and the previous one. pub value_change_in_timespan: Step, /// The amount of time passed between the previous and the current value. pub timespan: std::time::Duration, } impl Throughput { /// A convenience method to create a new ThroughPut from `value_change_in_timespan` and `timespan`. pub fn new(value_change_in_timespan: Step, timespan: std::time::Duration) -> Self { Throughput { value_change_in_timespan, timespan, } } } /// A way to display a [Unit]. #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)] pub struct Mode { location: Location, percent: bool, throughput: bool, } impl Mode { fn percent_location(&self) -> Option { if self.percent { Some(self.location) } else { None } } fn throughput_location(&self) -> Option { if self.throughput { Some(self.location) } else { None } } } /// initialization and modification impl Mode { /// Create a mode instance with percentage only. pub fn with_percentage() -> Self { Mode { percent: true, throughput: false, location: Location::AfterUnit, } } /// Create a mode instance with throughput only. pub fn with_throughput() -> Self { Mode { percent: false, throughput: true, location: Location::AfterUnit, } } /// Turn on percentage display on the current instance. pub fn and_percentage(mut self) -> Self { self.percent = true; self } /// Turn on throughput display on the current instance. pub fn and_throughput(mut self) -> Self { self.throughput = true; self } /// Change the display location to show up in front of the value. pub fn show_before_value(mut self) -> Self { self.location = Location::BeforeValue; self } } /// A utility to implement [Display][std::fmt::Display]. pub struct UnitDisplay<'a> { pub(crate) current_value: Step, pub(crate) upper_bound: Option, pub(crate) throughput: Option, pub(crate) parent: &'a Unit, pub(crate) display: What, } pub(crate) enum What { ValuesAndUnit, Unit, Values, } impl What { fn values(&self) -> bool { matches!(self, What::Values | What::ValuesAndUnit) } fn unit(&self) -> bool { matches!(self, What::Unit | What::ValuesAndUnit) } } impl<'a> UnitDisplay<'a> { /// Display everything, values and the unit. pub fn all(&mut self) -> &Self { self.display = What::ValuesAndUnit; self } /// Display only values. pub fn values(&mut self) -> &Self { self.display = What::Values; self } /// Display only units. pub fn unit(&mut self) -> &Self { self.display = What::Unit; self } } impl<'a> fmt::Display for UnitDisplay<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let unit: &dyn DisplayValue = self.parent.as_display_value(); let mode = self.parent.mode; let percent_location_and_fraction = self.upper_bound.and_then(|upper| { mode.and_then(|m| m.percent_location()) .map(|location| (location, ((self.current_value as f64 / upper as f64) * 100.0).floor())) }); let throughput_and_location = self.throughput.as_ref().and_then(|throughput| { mode.and_then(|m| m.throughput_location()) .map(|location| (location, throughput)) }); if self.display.values() { if let Some((Location::BeforeValue, fraction)) = percent_location_and_fraction { unit.display_percentage(f, fraction)?; f.write_char(' ')?; } if let Some((Location::BeforeValue, throughput)) = throughput_and_location { unit.display_throughput(f, throughput)?; f.write_char(' ')?; } unit.display_current_value(f, self.current_value, self.upper_bound)?; if let Some(upper) = self.upper_bound { unit.separator(f, self.current_value, self.upper_bound)?; unit.display_upper_bound(f, upper, self.current_value)?; } } if self.display.unit() { let mut buf = String::with_capacity(10); if self.display.values() { buf.write_char(' ')?; } unit.display_unit(&mut buf, self.current_value)?; if buf.len() > 1 { // did they actually write a unit? f.write_str(&buf)?; } if let Some((Location::AfterUnit, fraction)) = percent_location_and_fraction { f.write_char(' ')?; unit.display_percentage(f, fraction)?; } if let Some((Location::AfterUnit, throughput)) = throughput_and_location { f.write_char(' ')?; unit.display_throughput(f, throughput)?; } } Ok(()) } } prodash-28.0.0/src/unit/duration.rs000064400000000000000000000020461046102023000153160ustar 00000000000000use std::{fmt, time}; use humantime::format_duration; use crate::{progress::Step, unit::DisplayValue}; /// A marker for formatting numbers as duration in renderers, as in `7d4h20m10s`. #[derive(Copy, Clone, Default, Eq, PartialEq, Ord, PartialOrd, Debug)] pub struct Duration; impl DisplayValue for Duration { fn display_current_value(&self, w: &mut dyn fmt::Write, value: Step, _upper: Option) -> fmt::Result { w.write_str(&format_duration(time::Duration::new(value as u64, 0)).to_string()) } fn separator(&self, w: &mut dyn fmt::Write, _value: Step, _upper: Option) -> fmt::Result { w.write_str(" of ") } fn display_upper_bound(&self, w: &mut dyn fmt::Write, upper_bound: Step, _value: Step) -> fmt::Result { w.write_str(&format_duration(time::Duration::new(upper_bound as u64, 0)).to_string()) } fn dyn_hash(&self, state: &mut dyn std::hash::Hasher) { state.write(&[]) } fn display_unit(&self, _w: &mut dyn fmt::Write, _value: Step) -> fmt::Result { Ok(()) } } prodash-28.0.0/src/unit/human.rs000064400000000000000000000027611046102023000146050ustar 00000000000000use std::{fmt, fmt::Debug, hash::Hasher}; pub use human_format::{Formatter, Scales}; use crate::{progress::Step, unit::DisplayValue}; /// A helper for formatting numbers in a format easily read by humans in renderers, as in `2.54 million objects` #[derive(Debug)] pub struct Human { /// The name of the represented unit, like 'items' or 'objects'. pub name: &'static str, /// The formatter to format the actual numbers. pub formatter: Formatter, } impl Human { /// A convenience method to create a new new instance and its `formatter` and `name` fields. pub fn new(formatter: Formatter, name: &'static str) -> Self { Human { name, formatter } } fn format_bytes(&self, w: &mut dyn fmt::Write, value: Step) -> fmt::Result { let string = self.formatter.format(value as f64); for token in string.split(' ') { w.write_str(token)?; } Ok(()) } } impl DisplayValue for Human { fn display_current_value(&self, w: &mut dyn fmt::Write, value: Step, _upper: Option) -> fmt::Result { self.format_bytes(w, value) } fn display_upper_bound(&self, w: &mut dyn fmt::Write, upper_bound: Step, _value: Step) -> fmt::Result { self.format_bytes(w, upper_bound) } fn dyn_hash(&self, state: &mut dyn Hasher) { state.write(self.name.as_bytes()); state.write_u8(0); } fn display_unit(&self, w: &mut dyn fmt::Write, _value: Step) -> fmt::Result { w.write_str(self.name) } } prodash-28.0.0/src/unit/mod.rs000064400000000000000000000074671046102023000142640ustar 00000000000000use std::{fmt, ops::Deref, sync::Arc}; use crate::progress::Step; #[cfg(feature = "unit-bytes")] mod bytes; #[cfg(feature = "unit-bytes")] pub use bytes::Bytes; #[cfg(feature = "unit-duration")] mod duration; #[cfg(feature = "unit-duration")] pub use duration::Duration; #[cfg(feature = "unit-human")] /// pub mod human; #[cfg(feature = "unit-human")] #[doc(inline)] pub use human::Human; mod range; pub use range::Range; mod traits; pub use traits::DisplayValue; /// Various utilities to display values and units. pub mod display; /// A configurable and flexible unit for use in [Progress::init()][crate::Progress::init()]. #[derive(Debug, Clone, Hash)] pub struct Unit { kind: Kind, mode: Option, } /// Either a static label or a dynamic one implementing [`DisplayValue`]. #[derive(Clone)] pub enum Kind { /// Display only the given statically known label. Label(&'static str), /// Display a label created dynamically. Dynamic(Arc), } impl std::hash::Hash for Kind { fn hash(&self, state: &mut H) { match self { Kind::Label(s) => { 0.hash(state); s.dyn_hash(state) } Kind::Dynamic(label) => { 1.hash(state); label.dyn_hash(state); } } } } impl fmt::Debug for Kind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Kind::Label(name) => f.write_fmt(format_args!("Unit::Label({:?})", name)), Kind::Dynamic(_) => f.write_fmt(format_args!("Unit::Dynamic(..)")), } } } impl From<&'static str> for Unit { fn from(v: &'static str) -> Self { label(v) } } /// Returns a unit that is a static `label`. pub fn label(label: &'static str) -> Unit { Unit { kind: Kind::Label(label), mode: None, } } /// Returns a unit that is a static `label` along with information on where to display a fraction and throughput. pub fn label_and_mode(label: &'static str, mode: display::Mode) -> Unit { Unit { kind: Kind::Label(label), mode: Some(mode), } } /// Returns a unit that is a dynamic `label`. pub fn dynamic(label: impl DisplayValue + Send + Sync + 'static) -> Unit { Unit { kind: Kind::Dynamic(Arc::new(label)), mode: None, } } /// Returns a unit that is a dynamic `label` along with information on where to display a fraction and throughput. pub fn dynamic_and_mode(label: impl DisplayValue + Send + Sync + 'static, mode: display::Mode) -> Unit { Unit { kind: Kind::Dynamic(Arc::new(label)), mode: Some(mode), } } /// Display and utilities impl Unit { /// Create a representation of `self` implementing [`Display`][std::fmt::Display] in configurable fashion. /// /// * `current_value` is the progress value to display. /// * `upper_bound` is the possibly available upper bound of `current_value`. /// * `throughput` configures how throughput should be displayed if already available. /// /// Note that `throughput` is usually not available the first time a value is displayed. pub fn display( &self, current_value: Step, upper_bound: Option, throughput: impl Into>, ) -> display::UnitDisplay { display::UnitDisplay { current_value, upper_bound, throughput: throughput.into(), parent: self, display: display::What::ValuesAndUnit, } } /// Return `self` as trait object implementing `DisplayValue`. pub fn as_display_value(&self) -> &dyn DisplayValue { match self.kind { Kind::Label(ref unit) => unit, Kind::Dynamic(ref unit) => unit.deref(), } } } prodash-28.0.0/src/unit/range.rs000064400000000000000000000020341046102023000145620ustar 00000000000000use std::{fmt, hash::Hasher}; use crate::{progress::Step, unit::DisplayValue}; /// A helper for formatting numbers representing ranges in renderers as in `2 of 5 steps`. #[derive(Copy, Clone, Default, Eq, PartialEq, Ord, PartialOrd, Debug)] pub struct Range { /// The name of the unit to be appended to the range. pub name: &'static str, } impl Range { /// A convenience method to create a new instance of `name`. pub fn new(name: &'static str) -> Self { Range { name } } } impl DisplayValue for Range { fn display_current_value(&self, w: &mut dyn fmt::Write, value: Step, _upper: Option) -> fmt::Result { w.write_fmt(format_args!("{}", value + 1)) } fn separator(&self, w: &mut dyn fmt::Write, _value: Step, _upper: Option) -> fmt::Result { w.write_str(" of ") } fn dyn_hash(&self, state: &mut dyn Hasher) { self.name.dyn_hash(state) } fn display_unit(&self, w: &mut dyn fmt::Write, _value: Step) -> fmt::Result { w.write_str(self.name) } } prodash-28.0.0/src/unit/traits.rs000064400000000000000000000071711046102023000150030ustar 00000000000000use std::{fmt, hash::Hasher}; use crate::{progress::Step, unit::display}; /// A trait to encapsulate all capabilities needed to display a value with unit within a renderer. pub trait DisplayValue { /// Display the absolute `value` representing the current progress of an operation and write it to `w`. /// /// The `upper` bound is possibly provided when known to add context, even though it is not to be output /// as part of this method call. fn display_current_value(&self, w: &mut dyn fmt::Write, value: Step, _upper: Option) -> fmt::Result { fmt::write(w, format_args!("{}", value)) } /// Emit a token to separate two values. /// /// The `value` and its `upper` bound are provided to add context, even though it is not to be output /// as part of this method call. fn separator(&self, w: &mut dyn fmt::Write, _value: Step, _upper: Option) -> fmt::Result { w.write_str("/") } /// Emit the `upper_bound` to `w`. /// /// The `value` is provided to add context, even though it is not to be output as part of this method call. fn display_upper_bound(&self, w: &mut dyn fmt::Write, upper_bound: Step, _value: Step) -> fmt::Result { fmt::write(w, format_args!("{}", upper_bound)) } /// A way to hash our state without using generics. /// /// This helps to determine quickly if something changed. fn dyn_hash(&self, state: &mut dyn std::hash::Hasher); /// Emit the unit of `value` to `w`. /// /// The `value` is provided to add context, even though it is not to be output as part of this method call. fn display_unit(&self, w: &mut dyn fmt::Write, value: Step) -> fmt::Result; /// Emit `percentage` to `w`. fn display_percentage(&self, w: &mut dyn fmt::Write, percentage: f64) -> fmt::Result { w.write_fmt(format_args!("[{}%]", percentage as usize)) } /// Emit the `throughput` of an operation to `w`. fn display_throughput(&self, w: &mut dyn fmt::Write, throughput: &display::Throughput) -> fmt::Result { let (fraction, unit) = self.fraction_and_time_unit(throughput.timespan); w.write_char('|')?; self.display_current_value(w, throughput.value_change_in_timespan, None)?; w.write_char('/')?; match fraction { Some(fraction) => w.write_fmt(format_args!("{}", fraction)), None => Ok(()), }?; w.write_fmt(format_args!("{}|", unit)) } /// Given a `timespan`, return a fraction of the timespan based on the given unit, i.e. `(possible fraction, unit`). fn fraction_and_time_unit(&self, timespan: std::time::Duration) -> (Option, &'static str) { fn skip_one(v: f64) -> Option { if (v - 1.0).abs() < f64::EPSILON { None } else { Some(v) } } const HOUR_IN_SECS: u64 = 60 * 60; let secs = timespan.as_secs(); let h = secs / HOUR_IN_SECS; if h > 0 { return (skip_one(secs as f64 / HOUR_IN_SECS as f64), "h"); } const MINUTES_IN_SECS: u64 = 60; let m = secs / MINUTES_IN_SECS; if m > 0 { return (skip_one(secs as f64 / MINUTES_IN_SECS as f64), "m"); } if secs > 0 { return (skip_one(secs as f64), "s"); } (skip_one(timespan.as_millis() as f64), "ms") } } impl DisplayValue for &'static str { fn dyn_hash(&self, state: &mut dyn Hasher) { state.write(self.as_bytes()) } fn display_unit(&self, w: &mut dyn fmt::Write, _value: usize) -> fmt::Result { w.write_fmt(format_args!("{}", self)) } }