Skip to main content

cli_ui/prompt/
tasks.rs

1#![allow(missing_docs)]
2//! Sequential task runner — ports `@clack/prompts` `tasks()`.
3//!
4//! Each task gets its own spinner and runs in order; a task may return a final
5//! message that replaces its title on success.
6//!
7//! # Example
8//! ```rust,no_run
9//! use cli_ui::prompt::{tasks, Task};
10//!
11//! tasks(vec![
12//!     Task::new("Linting", |s| {
13//!         std::thread::sleep(std::time::Duration::from_millis(400));
14//!         s.message("Checking imports");
15//!         std::thread::sleep(std::time::Duration::from_millis(400));
16//!         Some("Lint clean".into())
17//!     }),
18//!     Task::new("Testing", |_| {
19//!         std::thread::sleep(std::time::Duration::from_millis(500));
20//!         Some("42 passing".into())
21//!     }),
22//! ]);
23//! ```
24
25use super::spinner::{spinner, Spinner};
26
27type TaskFn = Box<dyn FnOnce(&Spinner) -> Option<String> + Send + 'static>;
28
29pub struct Task {
30    title: String,
31    body: TaskFn,
32    enabled: bool,
33}
34
35impl Task {
36    pub fn new<F>(title: impl Into<String>, body: F) -> Self
37    where
38        F: FnOnce(&Spinner) -> Option<String> + Send + 'static,
39    {
40        Self {
41            title: title.into(),
42            body: Box::new(body),
43            enabled: true,
44        }
45    }
46
47    /// Skip this task when `false`.
48    pub fn enabled(mut self, v: bool) -> Self {
49        self.enabled = v;
50        self
51    }
52}
53
54/// Run a list of tasks in order, each with its own spinner.
55pub fn tasks(items: Vec<Task>) {
56    for t in items {
57        if !t.enabled {
58            continue;
59        }
60        let s = spinner();
61        s.start(&t.title);
62        let result = (t.body)(&s);
63        s.stop(result.unwrap_or(t.title));
64    }
65}