fast_rich/progress/
status.rs1use super::spinner::{Spinner, SpinnerStyle};
4use crate::style::Style;
5use std::io::{self, Write};
6use std::sync::atomic::{AtomicBool, Ordering};
7use std::sync::{Arc, Mutex};
8use std::thread;
9use std::time::Duration;
10
11pub struct Status {
15 message: Arc<Mutex<String>>,
17 spinner_style: SpinnerStyle,
19 running: Arc<AtomicBool>,
21 thread_handle: Option<thread::JoinHandle<()>>,
23}
24
25impl Status {
26 pub fn new(message: &str) -> Self {
28 Status {
29 message: Arc::new(Mutex::new(message.to_string())),
30 spinner_style: SpinnerStyle::Dots,
31 running: Arc::new(AtomicBool::new(false)),
32 thread_handle: None,
33 }
34 }
35
36 pub fn spinner_style(mut self, style: SpinnerStyle) -> Self {
38 self.spinner_style = style;
39 self
40 }
41
42 pub fn style(self, _style: Style) -> Self {
44 self
45 }
46
47 pub fn start(&mut self) {
49 if self.running.load(Ordering::SeqCst) {
50 return;
51 }
52
53 self.running.store(true, Ordering::SeqCst);
54
55 let running = self.running.clone();
56 let message = self.message.clone();
57 let spinner_style = self.spinner_style;
58
59 self.thread_handle = Some(thread::spawn(move || {
60 let spinner = Spinner::new("").style(spinner_style);
61 while running.load(Ordering::SeqCst) {
62 let frame = spinner.current_frame();
64 let msg = message.lock().unwrap().clone();
65 print!("\r\x1B[K{} {}", frame, msg);
66 let _ = io::stdout().flush();
67
68 thread::sleep(Duration::from_millis(spinner_style.interval_ms()));
69 }
70
71 print!("\r\x1B[K");
73 let _ = io::stdout().flush();
74 }));
75 }
76
77 pub fn stop(&mut self) {
79 self.running.store(false, Ordering::SeqCst);
80
81 if let Some(handle) = self.thread_handle.take() {
82 let _ = handle.join();
83 }
84 }
85
86 pub fn update(&mut self, message: &str) {
88 *self.message.lock().unwrap() = message.to_string();
89 }
90}
91
92impl Drop for Status {
93 fn drop(&mut self) {
94 self.stop();
95 }
96}
97
98pub struct StatusGuard {
102 status: Status,
103}
104
105impl StatusGuard {
106 pub fn new(message: &str) -> Self {
108 let mut status = Status::new(message);
109 status.start();
110 StatusGuard { status }
111 }
112
113 #[allow(dead_code)]
115 pub fn with_style(message: &str, spinner_style: SpinnerStyle) -> Self {
116 let mut status = Status::new(message).spinner_style(spinner_style);
117 status.start();
118 StatusGuard { status }
119 }
120
121 #[allow(dead_code)]
123 pub fn update(&mut self, message: &str) {
124 self.status.update(message);
125 }
126}
127
128impl Drop for StatusGuard {
129 fn drop(&mut self) {
130 self.status.stop();
131 }
132}
133
134pub fn with_status<T, F: FnOnce() -> T>(message: &str, f: F) -> T {
148 let _guard = StatusGuard::new(message);
149 f()
150}
151
152#[cfg(test)]
153mod tests {
154 use super::*;
155
156 #[test]
157 fn test_status_new() {
158 let status = Status::new("Testing...");
159 assert!(!status.running.load(Ordering::SeqCst));
160 }
161
162 #[test]
163 fn test_status_guard_drop() {
164 {
166 let _guard = StatusGuard::new("Test");
167 thread::sleep(Duration::from_millis(50));
168 }
169 }
171}