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.2"
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`]: make one with a total,
24//! advance it from anywhere, and a living reveal paints itself until you finish.
25//!
26//! ```no_run
27//! # #[cfg(feature = "terminal")] {
28//! use inkling::prelude::*;
29//!
30//! let loader = Loader::new(100);
31//! for _ in 0..100 {
32//! // ... a slice of work ...
33//! loader.inc(1);
34//! }
35//! loader.finish();
36//! # }
37//! ```
38//!
39//! Or wrap any iterator and forget about it:
40//!
41//! ```no_run
42//! # #[cfg(feature = "terminal")] {
43//! use inkling::prelude::*;
44//!
45//! for _item in (0..100).inkling() {
46//! // ... work ...
47//! }
48//! # }
49//! ```
50//!
51//! ## The one idea
52//!
53//! Everything turns on a single abstraction, the [`RankMap`]: every *ink* cell of
54//! the art is assigned a **reveal rank** in `0..=1`, and a cell is visible exactly
55//! when `rank <= progress`. Because rank is fixed and monotonic, the reveal can
56//! never run backwards, any progress value renders directly (it is seekable and
57//! resumable), and the whole thing is pure.
58//!
59//! *How* ranks are assigned is the single pluggable seam: an [`Ordering`]. The
60//! flagship [`Geodesic`](ordering::Geodesic) ordering traces the "spine" of the
61//! art and reveals along it, so a serpent paints from one tip to the other, around
62//! every coil, with no per-art configuration.
63//!
64//! ```
65//! use inkling::{Art, ordering::{Ordering, Geodesic}};
66//!
67//! let art = Art::parse("/\\__/\\\n\\____/");
68//! let ranks = Geodesic::default().rank(&art);
69//!
70//! // The pure, dependency-free view: render the half-revealed frame as text.
71//! let frame = inkling::frame::to_string(&art, &ranks, 0.5);
72//! assert_eq!(frame.lines().count(), art.height() as usize);
73//! ```
74//!
75//! ## Features
76//!
77//! | Feature | Default | What it adds |
78//! | --- | --- | --- |
79//! | `terminal` | yes | The live renderer: [`Loader`], [`render::Reveal`], colour. Pulls in `crossterm`. |
80//! | `unicode` | yes | Real display widths for CJK and emoji, via `unicode-width`. |
81//!
82//! With neither, the core ([`art`], [`rank`], [`ordering`], [`easing`],
83//! [`frame`], [`width`]) is pure `std` with no dependencies at all.
84
85pub mod art;
86pub mod easing;
87pub mod frame;
88pub mod ordering;
89pub mod rank;
90pub mod width;
91
92#[cfg(feature = "terminal")]
93mod guard;
94#[cfg(feature = "terminal")]
95pub mod loader;
96#[cfg(feature = "terminal")]
97pub mod render;
98
99pub use art::Art;
100pub use easing::Easing;
101pub use ordering::Ordering;
102pub use rank::RankMap;
103pub use width::glyph_cols;
104
105#[cfg(feature = "terminal")]
106pub use loader::{Handle, Loader, ProgressIteratorExt};
107#[cfg(feature = "terminal")]
108pub use render::{ColorDepth, Palette, Style};
109
110/// The handful of imports most programs want, in one glob:
111/// `use inkling::prelude::*;`.
112///
113/// Brings in `Loader`, the `.inkling()` iterator adaptor (via
114/// `ProgressIteratorExt`), the thread-safe `Handle`, and `Art`.
115pub mod prelude {
116 pub use crate::Art;
117 #[cfg(feature = "terminal")]
118 pub use crate::{Handle, Loader, ProgressIteratorExt, Style};
119}