1use std::io::{stderr, Write};
4use std::sync::mpsc::{self, Receiver, Sender};
5use std::thread;
6use std::time::Duration;
7
8#[derive(Debug)]
9pub struct Loading {
10 sender: Sender<Signal>,
11}
12
13impl Default for Loading {
14 fn default() -> Self {
15 Self::new(Spinner::default())
16 }
17}
18
19impl Loading {
20 pub fn new(spinner: Spinner) -> Self {
22 let (sender, receiver) = mpsc::channel();
23
24 Self::update_stdout(receiver);
25 Self::update_animation(sender.clone(), spinner);
26
27 Self { sender }
28 }
29
30 pub fn end(&self) {
32 let (sender, receiver) = mpsc::channel();
33 let _ = self.sender.send(Signal::Exit(sender));
34 let _ = receiver.recv();
36 }
37
38 pub fn text<T: ToString>(&self, text: T) {
40 let _ = self.sender.send(Signal::Text(text.to_string()));
41 }
42
43 pub fn success<T: ToString>(&self, text: T) {
45 let _ = self
46 .sender
47 .send(Signal::Next(Status::Success, text.to_string()));
48 }
49
50 pub fn fail<T: ToString>(&self, text: T) {
52 let _ = self
53 .sender
54 .send(Signal::Next(Status::Fail, text.to_string()));
55 }
56
57 pub fn warn<T: ToString>(&self, text: T) {
59 let _ = self
60 .sender
61 .send(Signal::Next(Status::Warn, text.to_string()));
62 }
63
64 pub fn info<T: ToString>(&self, text: T) {
66 let _ = self
67 .sender
68 .send(Signal::Next(Status::Info, text.to_string()));
69 }
70
71 pub fn debug<T: ToString>(&self, text: T) {
73 let text = format!("\x1B[90m{}\x1B[0m", text.to_string());
74
75 let _ = self.sender.send(Signal::Next(Status::Debug, text));
76 }
77
78 fn update_animation(sender: Sender<Signal>, mut spinner: Spinner) {
79 thread::spawn(move || {
80 while sender.send(Signal::Frame(spinner.next())).is_ok() {
81 thread::sleep(spinner.interval);
82 }
83 });
84 }
85
86 fn update_stdout(receiver: Receiver<Signal>) {
87 thread::spawn(move || {
88 let mut output = stderr();
89 let mut frame = "";
90 let mut text = String::new();
91
92 macro_rules! write_content {
93 () => {
94 let _ = output.write(b"\x1B[2K\x1B[0G");
95 let _ = output.flush();
96 };
97 ($($arg:tt)*) => {
98 let _ = output.write(b"\x1B[2K\x1B[0G");
99 let _ = output.write(format!($($arg)*).as_bytes());
100 let _ = output.flush();
101 };
102 }
103
104 let mut show_loader = true;
105 while let Ok(signal) = receiver.recv() {
106 match signal {
107 Signal::Frame(s) => {
108 frame = s;
109 if show_loader {
110 write_content!("[{}] {}", frame, text);
111 }
112 }
113 Signal::Text(s) => {
114 if show_loader {
115 write_content!("[{}] {}", frame, s);
116 }
117 text = s;
118 }
119 Signal::Next(status, s) => {
120 write_content!("[{}] {}\n", status.as_str(), s);
121 }
122 Signal::Exit(sender) => {
123 write_content!();
124 show_loader = false;
125 let _ = sender.send(());
126 }
128 }
129 }
130 });
131 }
132}
133
134#[derive(Debug)]
135enum Signal {
136 Frame(&'static str),
137 Text(String),
138 Next(Status, String),
139 Exit(Sender<()>),
140}
141
142#[derive(Debug, Clone)]
143pub struct Spinner {
144 index: usize,
145 frames: Vec<&'static str>,
146 interval: Duration,
147}
148
149impl Default for Spinner {
150 fn default() -> Self {
151 Self::new(vec!["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"])
152 }
153}
154
155impl Spinner {
156 pub fn new(frames: Vec<&'static str>) -> Self {
157 Self {
158 index: 0,
159 frames,
160 interval: Duration::from_millis(80),
161 }
162 }
163
164 pub fn interval(&mut self, interval: Duration) {
166 self.interval = interval
167 }
168
169 fn next(&mut self) -> &'static str {
170 match self.frames.get(self.index) {
171 Some(s) => {
172 self.index += 1;
173 s
174 }
175 None => {
176 self.index = 1;
177 self.frames[0]
178 }
179 }
180 }
181}
182
183#[derive(Debug)]
184enum Status {
185 Success,
186 Fail,
187 Warn,
188 Info,
189 Debug,
190}
191
192impl Status {
193 fn as_str(&self) -> &'static str {
194 match self {
195 Status::Success => "\x1B[92m+\x1B[0m",
196 Status::Fail => "\x1B[91mFAIL\x1B[0m",
197 Status::Warn => "\x1B[93m!\x1B[0m",
198 Status::Info => "\x1B[94m*\x1B[0m",
199 Status::Debug => "\x1B[90m \x1B[0m",
200 }
201 }
202}