//! Progress integration for streams. //! //! Four entry points on [`StreamExt`]: //! //! - [`progress`](StreamExt::progress) takes a fraction closure; appropriate when each item //! already carries enough information to compute completion in `0.0..=1.0`. Sugar for //! `self.progressive(fraction_fn).with_theme(theme)`. //! - [`progress_count`](StreamExt::progress_count) counts items internally; pair with //! [`with_len`](ProgressCountStream::with_len) to derive the bar fraction from `count / total`. //! The closure-free path for "I know how many items will flow through". //! - [`progress_bytes`](StreamExt::progress_bytes) takes a byte-delta closure. The builder owns //! the cumulative counter, EWMA rate and (when //! [`with_len`](ProgressBytesStream::with_len) is set) the derived progress fraction. Pair with //! [`Segment::bytes`](crate::layout::Segment::bytes), //! [`Segment::rate`](crate::layout::Segment::rate) and [`Segment::eta`](crate::layout::Segment::eta) //! in a custom [`Layout`](crate::layout::Layout) for byte / throughput / ETA columns. //! - [`progressive`](StreamExt::progressive), [`progressive_count`](StreamExt::progressive_count) //! and [`progressive_bytes`](StreamExt::progressive_bytes) produce unconfigured adapters. Without //! [`with_theme`](ProgressStream::with_theme) they inherit the parent [`Group`]'s theme; with //! `with_theme` they render standalone or override the Group's theme per-row. //! //! Dynamic messages compose on top of any builder via //! [`with_messages`](ProgressStream::with_messages). pub mod group; pub use group::Group; use std::borrow::Cow; use std::fmt::Display; use std::io::IsTerminal; use std::pin::Pin; use std::task::{Context, Poll}; use std::time::Duration; use futures_lite::stream::Pending; use futures_lite::{stream, Stream}; use owo_colors::Style; use pin_project_lite::pin_project; use crate::line::{FrameContext, Line}; use crate::progressive::Progressive; use crate::spinner::Ticks; use crate::state::State; use crate::term::CursorGuard; use crate::Theme; /// Materialised rendering bits used by the standalone path. struct Rendering<'a> { line: Line<'a>, ticks: Ticks<'a>, spinner_char: Option, spinner_style: Style, annotation_style: Style, is_tty: bool, _guard: CursorGuard, } /// Lifecycle of the standalone rendering bits. enum RenderingState<'a> { /// Constructed but not yet polled. Materialise on first poll using the row's theme override /// (or [`Theme::default()`] when none was set). Pending, /// Materialised; standalone rendering is active. Active(Rendering<'a>), /// A [`Group`] owns rendering for this row; no standalone rendering will happen. Detached, } pin_project! { /// A [`Stream`] wrapped to track progress derived from a fraction closure. pub struct ProgressStream<'a, S, F, M = Pending<&'static str>> { #[pin] inner: S, fraction_fn: F, #[pin] messages: M, state: State, current: usize, theme_override: Option>, spinner_style_override: Option