inkling/lib.rs
1//! # inkling
2//!
3//! Reveal arbitrary ASCII art as a progress indicator.
4//!
5//! A normal progress bar maps a scalar `0..=1` onto *how much of a line is
6//! filled*. `inkling` generalises that to two dimensions: it maps progress onto
7//! **the order in which the glyphs of a picture appear**. Give it a dragon and it
8//! paints the dragon as your task runs.
9//!
10//! ## Install
11//!
12//! ```toml
13//! [dependencies]
14//! inkling-loader = "0.1"
15//! ```
16//!
17//! It publishes as `inkling-loader` (the short name was taken) but imports as `inkling`.
18//! Want the command-line tool rather than the library? That is the `inkling-cli` crate
19//! (`cargo install inkling-cli`), which lets any language drive a reveal through a pipe.
20//!
21//! ## Quick start
22//!
23//! The easy front door is [`Loader`](loader::Loader): make one with a total,
24//! advance it from anywhere, and a living reveal paints itself until you finish.
25//!
26//! ```no_run
27//! use inkling::prelude::*;
28//!
29//! let loader = Loader::new(100);
30//! for _ in 0..100 {
31//! // ... a slice of work ...
32//! loader.inc(1);
33//! }
34//! loader.finish();
35//! ```
36//!
37//! Or wrap any iterator and forget about it:
38//!
39//! ```no_run
40//! use inkling::prelude::*;
41//!
42//! for _item in (0..100).inkling() {
43//! // ... work ...
44//! }
45//! ```
46//!
47//! ## The one idea
48//!
49//! Everything turns on a single abstraction, the [`RankMap`]: every *ink* cell of
50//! the art is assigned a **reveal rank** in `0..=1`, and a cell is visible exactly
51//! when `rank <= progress`. Because rank is fixed and monotonic, the reveal can
52//! never run backwards, any progress value renders directly (it is seekable and
53//! resumable), and the whole thing is pure.
54//!
55//! *How* ranks are assigned is the single pluggable seam: an [`Ordering`]. The
56//! flagship [`Geodesic`](ordering::Geodesic) ordering traces the "spine" of the
57//! art and reveals along it, so a serpent paints from one tip to the other, around
58//! every coil, with no per-art configuration.
59//!
60//! ```
61//! use inkling::{Art, ordering::{Ordering, Geodesic}};
62//!
63//! let art = Art::parse("/\\__/\\\n\\____/");
64//! let ranks = Geodesic::default().rank(&art);
65//!
66//! // The pure, dependency-free view: render the half-revealed frame as text.
67//! let frame = inkling::frame::to_string(&art, &ranks, 0.5);
68//! assert_eq!(frame.lines().count(), art.height() as usize);
69//! ```
70
71pub mod art;
72pub mod easing;
73pub mod frame;
74pub mod ordering;
75pub mod rank;
76
77#[cfg(feature = "terminal")]
78pub mod loader;
79#[cfg(feature = "terminal")]
80pub mod render;
81
82pub use art::Art;
83pub use easing::Easing;
84pub use ordering::Ordering;
85pub use rank::RankMap;
86
87#[cfg(feature = "terminal")]
88pub use loader::{Handle, Loader, ProgressIteratorExt};
89
90/// The handful of imports most programs want, in one glob:
91/// `use inkling::prelude::*;`.
92///
93/// Brings in `Loader`, the `.inkling()` iterator adaptor (via
94/// `ProgressIteratorExt`), the thread-safe `Handle`, and `Art`.
95pub mod prelude {
96 pub use crate::Art;
97 #[cfg(feature = "terminal")]
98 pub use crate::{Handle, Loader, ProgressIteratorExt};
99}