Skip to main content

cli_ui/prompt/
task_log.rs

1#![allow(missing_docs)]
2//! Task with a collapsible running log — ports `@clack/prompts` `taskLog()`.
3//!
4//! The log lines are shown while the task runs. On `success()` they are
5//! cleared and replaced with a single success marker; on `error()` they are
6//! preserved so the user can read what happened.
7
8use crate::styles::{paint, DIM, ERR, OK, WHITE};
9use std::io::Write;
10use std::sync::Mutex;
11
12const BAR: &str = "│";
13const STEP_OK: &str = "◇";
14const STEP_GREEN: &str = "◆";
15const STEP_ERR: &str = "■";
16
17pub struct TaskLog {
18    title: String,
19    limit: usize,
20    lines: Mutex<Vec<String>>,
21}
22
23pub fn task_log(title: impl Into<String>) -> TaskLog {
24    let t = TaskLog {
25        title: title.into(),
26        limit: 10,
27        lines: Mutex::new(Vec::new()),
28    };
29    t.render_intro();
30    t
31}
32
33impl TaskLog {
34    /// Cap visible lines while the task is running.
35    pub fn limit(mut self, n: usize) -> Self {
36        self.limit = n.max(1);
37        self
38    }
39
40    fn render_intro(&self) {
41        let mut out = std::io::stderr();
42        let _ = writeln!(out, "{}", paint(DIM, BAR));
43        let _ = writeln!(
44            out,
45            "{}  {}",
46            paint(OK, STEP_GREEN),
47            paint(WHITE, &self.title)
48        );
49        let _ = writeln!(out, "{}", paint(DIM, BAR));
50    }
51
52    pub fn message(&self, msg: impl AsRef<str>) {
53        let mut lines = self.lines.lock().unwrap();
54        for ln in msg.as_ref().split('\n') {
55            lines.push(ln.to_string());
56        }
57        // trim oldest
58        while lines.len() > self.limit {
59            lines.remove(0);
60        }
61        let mut out = std::io::stderr();
62        let _ = writeln!(out, "{}  {}", paint(DIM, BAR), paint(DIM, msg.as_ref()));
63    }
64
65    pub fn success(&self, msg: impl AsRef<str>) {
66        let mut out = std::io::stderr();
67        let _ = writeln!(
68            out,
69            "{}  {}",
70            paint(OK, STEP_OK),
71            paint(WHITE, msg.as_ref())
72        );
73    }
74
75    pub fn error(&self, msg: impl AsRef<str>) {
76        let mut out = std::io::stderr();
77        let _ = writeln!(
78            out,
79            "{}  {}",
80            paint(ERR, STEP_ERR),
81            paint(ERR, msg.as_ref())
82        );
83        // dump retained lines under the error
84        let lines = self.lines.lock().unwrap();
85        for ln in lines.iter() {
86            let _ = writeln!(out, "{}  {}", paint(DIM, BAR), paint(DIM, ln));
87        }
88    }
89}