|
2 | 2 |
|
3 | 3 | /*!
|
4 | 4 |
|
5 |
| -Nutmeg draws terminal progress bars whose appearance is completely controlled |
6 |
| -by the application. |
| 5 | +Nutmeg draws multi-line terminal progress bars to an ANSI terminal. |
| 6 | +
|
| 7 | +By contrast to other Rust progress-bar libraries, Nutmeg has no built-in |
| 8 | +concept of what the progress bar or indicator should look like: the application |
| 9 | +has complete control. |
7 | 10 |
|
8 | 11 | # Concept
|
9 | 12 |
|
10 |
| -By contrast to other Rust progress-bar libraries, Nutmeg has no built-in |
11 |
| -concept of what the progress bar or indicator should look like: this is |
12 |
| -entirely under the control of the application. |
| 13 | +Nutmeg has three key types: Model, View, and Options. |
13 | 14 |
|
14 |
| -The application is responsible for: |
| 15 | +## Model |
15 | 16 |
|
16 |
| -1. Defining a "model" type that holds whatever information is relevant to drawing |
17 |
| - progress bars: the time elapsed, number of things processed, currently active tasks, |
18 |
| - total expected work, whatever... |
19 |
| -2. Implementing the [Model] trait for your model. This has only one mandatory method, |
20 |
| - [Model::render], which renders the model into styled text. |
21 |
| -3. Constructing a [View] to draw a progress bar. |
22 |
| -4. Updating the model when appropriate by calling [View::update], passing a callback |
23 |
| - that mutates the state. |
24 |
| -5. Printing any messages while the [View] is in use |
25 |
| - via `writeln!(view, ...)` or [View::message]. |
| 17 | +A type implementing the [Model] trait holds whatever information is needed to draw the |
| 18 | +progress bars. This might be the start time of the operation, the number of things |
| 19 | +processed, the amount of data transmitted or received, the currently active tasks, whatever... |
26 | 20 |
|
27 |
| -Some applications might find the provided [models] suit their needs, in which case they |
28 |
| -can skip steps 1 and 2. |
| 21 | +The Model can be any of these things, from simplest to most powerful: |
29 | 22 |
|
30 |
| -The application can control colors and styling by including ANSI |
31 |
| -escape sequences in the rendered string, for example by using the |
32 |
| -`yansi` crate. |
| 23 | +1. Any type that implements [std::fmt::Display], such as a String or integer. |
| 24 | +2. One of the provided [models]. |
| 25 | +3. An application-defined struct (or enum or other type) that implements [Model]. |
| 26 | +
|
| 27 | +The model is responsible for rendering itself into a String, optionally with ANSI styling, |
| 28 | +by implementing [Model::render] (or [std::fmt::Display]). Applications might |
| 29 | +choose to use any of the Rust crates that can render ANSI control codes into a |
| 30 | +string, such as yansi. |
33 | 31 |
|
34 | 32 | The application is responsible for deciding whether or not to
|
35 |
| -color its output, for example by consulting `$CLICOLORS`. |
| 33 | +color its output, for example by consulting `$CLICOLORS` or its own command line. |
| 34 | +
|
| 35 | +Models can optionally provide a "final message" by implementing |
| 36 | +[Model::final_message], which will be left on the screen when the view is finished. |
| 37 | +
|
| 38 | +If one overall operation represents several concurrent operations then the |
| 39 | +application can, for example, represent them in a collection within the Model, and |
| 40 | +render them into multiple lines, or multiple sections in a single line. |
| 41 | +(See `examples/multithreaded.rs`.) |
36 | 42 |
|
37 |
| -The Nutmeg library is responsible for: |
| 43 | +## View |
38 | 44 |
|
39 |
| -* Periodically drawing the progress bar in response to updates, including |
40 |
| - horizontally truncating output to fit on the screen. |
41 |
| -* Removing the progress bar when the view is finished or dropped. |
42 |
| -* Coordinating to hide the bar to print text output, and restore it |
43 |
| - afterwards. |
44 |
| -* Limiting the rate at which updates are drawn to the screen. |
45 |
| -* Disabling progress bars if stdout is not a terminal. |
| 45 | +To get the model on to the terminal the application must create a [View], typically |
| 46 | +with [View::new], passing the initial model. The view takes ownership of the model. |
| 47 | +
|
| 48 | +The application then updates the model state via [View::update], which may decide |
| 49 | +to paint the view to the terminal, subject to rate-limiting and other constraints. |
| 50 | +
|
| 51 | +The view has an internal mutex and is `Send` and `Sync`, |
| 52 | +so it can be shared freely across threads. |
| 53 | +
|
| 54 | +The view automatically erases itself from the screen when it is dropped. |
| 55 | +
|
| 56 | +While the view is on the screen, the application can print messages interleaved |
| 57 | +with the progress bar by either calling [View::message], or treating it as a [std::io::Write] |
| 58 | +destination, for example for [std::writeln]. |
46 | 59 |
|
47 | 60 | Errors in writing to the terminal cause a panic.
|
48 | 61 |
|
49 |
| -Nutmeg only supports ANSI terminals, which are supported on all Unix |
50 |
| -and Windows 10 and later. |
| 62 | +## Options |
| 63 | +
|
| 64 | +A small [Options] type, passed to the View constructor, allows turning progress bars |
| 65 | +off, setting rate limits, etc. |
| 66 | +
|
| 67 | +In particular applications might choose to construct all [Options] from a single function |
| 68 | +that respects an application-level option for whether progress bars should be drawn. |
| 69 | +
|
| 70 | +## Utility functions |
| 71 | +
|
| 72 | +This crate also provides a few free functions such as [estimate_remaining], |
| 73 | +that can be helpful in implementing [Model::render]. |
51 | 74 |
|
52 | 75 | # Example
|
53 | 76 |
|
@@ -96,16 +119,36 @@ fn main() -> std::io::Result<()> {
|
96 | 119 |
|
97 | 120 | See the `examples/` directory for more.
|
98 | 121 |
|
99 |
| -# Other features |
| 122 | +# Performance |
100 | 123 |
|
101 |
| -The [models] module provides some predefined models, for example counting `i` of `n` items |
102 |
| -of work complete with an extrapolated ETA. |
| 124 | +Nutmeg's goal is that [View::update] is cheap enough that applications can call it |
| 125 | +fairly freely when there are small updates. The library takes care of rate-limiting |
| 126 | +updates to the terminal, as configured in the [Options]. |
103 | 127 |
|
104 |
| -Models can optionally provide a "final message" by implementing [Model::final_message], which |
105 |
| -will be left on the screen when the view is finished. |
| 128 | +Each call to [View::update] will take a `parking_lot` mutex and check the |
| 129 | +system time, in addition to running the callback and some function-call overhead. |
106 | 130 |
|
107 |
| -This crate also provides a few free functions such as [estimate_remaining], |
108 |
| -that can be helpful in rendering progress bars. |
| 131 | +The model is only rendered to a string, and the string printed to a terminal, if |
| 132 | +sufficient time has passed since it was last painted. |
| 133 | +
|
| 134 | +The `examples/bench.rs` sends updates as fast as possible to a model containing a |
| 135 | +single `u64`, from a single thread. As of 2022-03-22, on a 2019 Core i9 Macbook Pro, |
| 136 | +it takes about 500ms to send 10e6 updates, or 50ns/update. |
| 137 | +
|
| 138 | +# Project status |
| 139 | +
|
| 140 | +Nutmeg is a young library. Although the API will not break gratuitously, |
| 141 | +it may evolve in response to experience and feedback in every pre-1.0 release. |
| 142 | +
|
| 143 | +If the core ideas prove useful and the API remains stable for an extended period |
| 144 | +then the author intends to promote it to 1.0, after which the API will respect |
| 145 | +Rust stability conventions. |
| 146 | +
|
| 147 | +Changes are described in the [changelog](#Changelog) in the top-level Rustdoc, |
| 148 | +below. |
| 149 | +
|
| 150 | +Constructive feedback on integrations that work well, or that don't work well, |
| 151 | +is welcome. |
109 | 152 |
|
110 | 153 | # Potential future features
|
111 | 154 |
|
|
0 commit comments