sub_solver/
loading.rs

1/// Source: https://github.com/wyhaya/loading/blob/main/src/lib.rs
2/// Formatting altered slightly
3use 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    /// Create a stdout loading
21    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    /// End loading
31    pub fn end(&self) {
32        let (sender, receiver) = mpsc::channel();
33        let _ = self.sender.send(Signal::Exit(sender));
34        // Waiting for the sub -thread to exit
35        let _ = receiver.recv();
36    }
37
38    /// Modify the currently displayed text
39    pub fn text<T: ToString>(&self, text: T) {
40        let _ = self.sender.send(Signal::Text(text.to_string()));
41    }
42
43    /// Save the current line as 'success' and continue to load on the next line
44    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    /// Save the current line as 'fail' and continue to load on the next line
51    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    /// Save the current line as 'warn' and continue to load on the next line
58    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    /// Save the current line as 'info' and continue to load on the next line
65    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    /// Save the current line as 'debug' and continue to load on the next line
72    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                        // break;
127                    }
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    /// Change the interval between two frames
165    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}