//! Spinner integration for futures. //! //! Import [`FutureExt`] to wrap any [`Future`] with progress display. Two entry points: //! //! - [`progress(theme)`](FutureExt::progress) is sugar for `progressive().with_theme(theme)`: //! awaiting the returned [`ProgressFuture`] drives a spinner, optional bar and message on its //! own terminal line and resolves to the wrapped future's output. //! - [`progressive()`](FutureExt::progressive) returns an unconfigured [`ProgressFuture`]. Without //! a [`with_theme`](ProgressFuture::with_theme) call it inherits the parent [`Group`]'s theme, //! with one, it overrides per-row. pub mod group; pub mod join; pub use group::Group; pub use join::{join, Join}; use std::borrow::Cow; use std::fmt::Display; use std::future::Future; 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: line, spinner ticks, cursor guard. pub(super) struct Rendering<'a> { pub(super) line: Line<'a>, pub(super) ticks: Ticks<'a>, pub(super) spinner_char: Option, pub(super) spinner_style: Style, pub(super) annotation_style: Style, pub(super) is_tty: bool, pub(super) _guard: CursorGuard, } /// Lifecycle of the standalone rendering bits. pub(super) 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 [`Future`] wrapped with progress state. /// /// `ProgressFuture` carries the wrapped future together with state read out via [`Progressive`] /// and (lazily) the rendering machinery for standalone use. The `M` and `P` parameters track the /// optional message and progress stream types and default to [`Pending`] (a ZST that never /// yields) so the bare `fut.progress(theme).await` path allocates nothing beyond the spinner /// [`Ticks`] state. pub struct ProgressFuture<'a, F, M = Pending<&'static str>, P = Pending> { #[pin] inner: F, #[pin] messages: M, #[pin] progress: P, state: State, theme_override: Option>, spinner_style_override: Option