1use crossterm::terminal::Clear;
2use crossterm::{cursor, execute, style::Print, terminal::ClearType};
3use std::io::{stdout, Write};
4use std::sync::{
5 atomic::{AtomicBool, Ordering},
6 Arc,
7};
8use std::thread;
9use std::time::Duration;
10
11pub struct Spinner {
12 frames: Vec<&'static str>,
13 is_spinning: Arc<AtomicBool>,
14 position: (u16, u16),
15}
16
17impl Spinner {
18 pub fn new() -> Self {
19 let (x, y) = cursor::position().unwrap();
20 Self {
21 frames: vec!["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"],
22 is_spinning: Arc::new(AtomicBool::new(false)),
23 position: (x, y),
24 }
25 }
26
27 pub fn render(&self) {
28 let is_spinning = self.is_spinning.clone();
29 let position: (u16, u16) = self.position;
30 let frames = self.frames.clone();
31
32 is_spinning.store(true, Ordering::Relaxed);
33
34 thread::spawn(move || {
35 let mut current_frame = 0;
36 while is_spinning.load(Ordering::Relaxed) {
37 let frame = frames[current_frame];
38 let mut stdout = stdout();
39 execute!(
40 stdout,
41 cursor::MoveTo(position.0, position.1),
42 Clear(ClearType::CurrentLine),
43 Print(frame)
44 )
45 .unwrap();
46 stdout.flush().unwrap();
47 current_frame = (current_frame + 1) % frames.len();
48 thread::sleep(Duration::from_millis(100));
49 }
50 });
51 }
52
53 pub fn stop(&self) {
54 execute!(
55 stdout(),
56 cursor::MoveTo(self.position.0, self.position.1),
57 Clear(ClearType::CurrentLine)
58 )
59 .unwrap();
60 self.is_spinning.store(false, Ordering::Relaxed);
61 }
62}