![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** is a dashboard for displaying progress of concurrent applications.
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 --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-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** _(default)_
* 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
* **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_
* **ctrlc**
* 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 `compound_duration` 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
## Transition to futures-lite
In order for the build-time improvements to become effective, we need…
* **crosstermion**
* `StreamExt::filter_map(…)`
* **prodash::tui::engine**
* `stream::select_all(…)`
* `StreamExt::map(…)`
* `StreamExt::boxed(…)`
* **prodash -> example -> dashboard**
* `stream::select(…)`
* `future::select(…)`
* `future::join_all(…)`
* `FutureExt::boxed(…)`
## 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.