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}