Skip to main content

rusty_rich/
status.rs

1//! Status — status message with spinner. Equivalent to Rich's `status.py`.
2
3use std::io::{self, Write};
4use std::time::Instant;
5
6use crate::spinner::Spinner;
7
8/// A status message rendered with an animated spinner.
9///
10/// Usage:
11/// ```rust,no_run
12/// # use rusty_rich::Status;
13/// let mut status = Status::new("Working...");
14/// status.start();
15/// // do work...
16/// status.update("Still working...");
17/// status.stop();
18/// ```
19pub struct Status {
20    pub spinner: Spinner,
21    pub status: String,
22    pub started: Option<Instant>,
23}
24
25impl std::fmt::Debug for Status {
26    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27        f.debug_struct("Status")
28            .field("status", &self.status)
29            .field("started", &self.started)
30            .finish()
31    }
32}
33
34impl Status {
35    /// Create a new Status with the given message.
36    pub fn new(status: impl Into<String>) -> Self {
37        Self {
38            spinner: Spinner::default(),
39            status: status.into(),
40            started: None,
41        }
42    }
43
44    /// Builder: replace the default spinner with a custom [`Spinner`].
45    pub fn spinner(mut self, spinner: Spinner) -> Self {
46        self.spinner = spinner;
47        self
48    }
49
50    /// Start displaying the status.
51    pub fn start(&mut self) -> io::Result<()> {
52        self.started = Some(Instant::now());
53        self.write_status()
54    }
55
56    /// Update the status message.
57    pub fn update(&mut self, status: impl Into<String>) -> io::Result<()> {
58        self.status = status.into();
59        self.write_status()
60    }
61
62    /// Stop the status display (clears the line).
63    pub fn stop(&mut self) -> io::Result<()> {
64        // Carriage return + clear line
65        let _ = write!(io::stdout(), "\r\x1b[K");
66        let _ = io::stdout().flush();
67        self.started = None;
68        Ok(())
69    }
70
71    /// Refresh the display.
72    pub fn refresh(&mut self) -> io::Result<()> {
73        self.write_status()
74    }
75
76    fn write_status(&mut self) -> io::Result<()> {
77        let elapsed = self.started.map(|s| s.elapsed()).unwrap_or_default();
78        let spinner_str = self.spinner.render(elapsed);
79        write!(io::stdout(), "\r{spinner_str} {}", self.status)?;
80        io::stdout().flush()
81    }
82}