spinners_jdxcode/lib.rs
1use std::thread::JoinHandle;
2use std::time::Instant;
3use std::{
4 sync::mpsc::{channel, Sender, TryRecvError},
5 thread,
6 time::Duration,
7};
8
9pub use crate::utils::spinner_names::SpinnerNames as Spinners;
10use crate::utils::spinners_data::SPINNERS as SpinnersMap;
11pub use crate::utils::stream::Stream;
12
13mod utils;
14
15pub struct Spinner {
16 sender: Sender<(Instant, Option<String>)>,
17 join: Option<JoinHandle<()>>,
18 stream: Stream
19}
20
21impl Drop for Spinner {
22 fn drop(&mut self) {
23 if self.join.is_some() {
24 self.sender.send((Instant::now(), None)).unwrap();
25 self.join.take().unwrap().join().unwrap();
26 }
27 }
28}
29
30impl Spinner {
31 /// Create a new spinner along with a message
32 ///
33 /// # Examples
34 ///
35 /// Basic Usage:
36 ///
37 /// ```
38 /// use spinners::{Spinner, Spinners};
39 ///
40 /// let sp = Spinner::new(Spinners::Dots, "Loading things into memory...".into());
41 /// ```
42 ///
43 /// No Message:
44 ///
45 /// ```
46 /// use spinners::{Spinner, Spinners};
47 ///
48 /// let sp = Spinner::new(Spinners::Dots, String::new());
49 /// ```
50 pub fn new(spinner: Spinners, message: String) -> Self {
51 Self::new_inner(spinner, message, None, None)
52 }
53
54 /// Create a new spinner that logs the time since it was created
55 pub fn with_timer(spinner: Spinners, message: String) -> Self {
56 Self::new_inner(spinner, message, Some(Instant::now()), None)
57 }
58
59 /// Creates a new spinner along with a message with a specified output stream
60 ///
61 /// # Examples
62 ///
63 /// Basic Usage:
64 ///
65 /// ```
66 /// use spinners::{Spinner, Spinners, Stream};
67 ///
68 /// let sp = Spinner::with_stream(Spinners::Dots, String::new(), Stream::Stderr);
69 /// ```
70 pub fn with_stream(spinner: Spinners, message: String, stream: Stream) -> Self {
71 Self::new_inner(spinner, message, None, Some(stream))
72 }
73
74 /// Creates a new spinner that logs the time since it was created with a specified output stream
75 ///
76 /// # Examples
77 ///
78 /// Basic Usage:
79 ///
80 /// ```
81 /// use spinners::{Spinner, Spinners, Stream};
82 ///
83 /// let sp = Spinner::with_timer_and_stream(Spinners::Dots, String::new(), Stream::Stderr);
84 /// ```
85 pub fn with_timer_and_stream(spinner: Spinners, message: String, stream: Stream) -> Self {
86 Self::new_inner(spinner, message, Some(Instant::now()), Some(stream))
87 }
88
89 fn new_inner(spinner: Spinners, message: String, start_time: Option<Instant>, stream: Option<Stream>) -> Self
90 {
91 let spinner_name = spinner.to_string();
92 let spinner_data = SpinnersMap
93 .get(&spinner_name)
94 .unwrap_or_else(|| panic!("No Spinner found with the given name: {}", spinner_name));
95
96 let stream = if let Some(stream) = stream { stream } else { Stream::default() };
97
98 let (sender, recv) = channel::<(Instant, Option<String>)>();
99
100 let join = thread::spawn(move || 'outer: loop {
101
102 for frame in spinner_data.frames.iter() {
103 let (do_stop, stop_time, stop_symbol) = match recv.try_recv() {
104 Ok((stop_time, stop_symbol)) => (true, Some(stop_time), stop_symbol),
105 Err(TryRecvError::Disconnected) => (true, None, None),
106 Err(TryRecvError::Empty) => (false, None, None),
107 };
108
109 let frame = stop_symbol.unwrap_or_else(|| frame.to_string());
110
111 stream.write(&frame, &message, start_time, stop_time).expect("IO Error");
112
113 if do_stop {
114 break 'outer;
115 }
116
117 thread::sleep(Duration::from_millis(spinner_data.interval as u64));
118 }
119 });
120
121 Self {
122 sender,
123 join: Some(join),
124 stream
125 }
126 }
127
128 /// Stops the spinner
129 ///
130 /// Stops the spinner that was created with the [`Spinner::new`] function.
131 ///
132 /// Optionally call [`stop_with_newline`] to print a newline after the spinner is stopped,
133 /// or the [`stop_with_message`] function to print a message after the spinner is stopped.
134 ///
135 /// [`Spinner::new`]: struct.Spinner.html#method.new
136 /// [`stop_with_newline`]: struct.Spinner.html#method.stop_with_newline
137 /// [`stop_with_message`]: struct.Spinner.html#method.stop_with_message
138 ///
139 /// # Examples
140 ///
141 /// Basic Usage:
142 ///
143 /// ```
144 /// use spinners::{Spinner, Spinners};
145 ///
146 /// let mut sp = Spinner::new(Spinners::Dots, "Loading things into memory...".into());
147 ///
148 /// sp.stop();
149 /// ```
150 pub fn stop(&mut self) {
151 self.stop_inner(Instant::now(), None);
152 }
153
154 /// Stop with a symbol that replaces the spinner
155 ///
156 /// The symbol is a String rather than a Char to allow for more flexibility, such as using ANSI color codes.
157 ///
158 /// # Examples
159 ///
160 /// Basic Usage:
161 ///
162 /// ```
163 /// use spinners::{Spinner, Spinners};
164 ///
165 /// let mut sp = Spinner::new(Spinners::Dots, "Loading things into memory...".into());
166 ///
167 /// sp.stop_with_symbol("🗸");
168 /// ```
169 ///
170 /// ANSI colors (green checkmark):
171 ///
172 /// ```
173 /// use spinners::{Spinner, Spinners};
174 ///
175 /// let mut sp = Spinner::new(Spinners::Dots, "Loading things into memory...".into());
176 ///
177 /// sp.stop_with_symbol("\x1b[32m🗸\x1b[0m");
178 /// ```
179 pub fn stop_with_symbol(&mut self, symbol: &str) {
180 self.stop_inner(Instant::now(), Some(symbol.to_owned()));
181 self.stream.stop(None, Some(symbol)).expect("IO error");
182 }
183
184 /// Stops the spinner and prints a new line
185 ///
186 /// # Examples
187 ///
188 /// Basic Usage:
189 ///
190 /// ```
191 /// use spinners::{Spinner, Spinners};
192 ///
193 /// let mut sp = Spinner::new(Spinners::Dots, "Loading things into memory...".into());
194 ///
195 /// sp.stop_with_newline();
196 /// ```
197 pub fn stop_with_newline(&mut self) {
198 self.stop();
199 self.stream.stop(None, None).expect("IO error");
200 }
201
202 /// Stops the spinner and prints the provided message
203 ///
204 /// # Examples
205 ///
206 /// Basic Usage:
207 ///
208 /// ```
209 /// use spinners::{Spinner, Spinners};
210 ///
211 /// let mut sp = Spinner::new(Spinners::Dots, "Loading things into memory...".into());
212 ///
213 /// sp.stop_with_message("Finished loading things into memory!".into());
214 /// ```
215 pub fn stop_with_message(&mut self, msg: String) {
216 self.stop();
217 self.stream.stop(Some(&msg), None).expect("IO Error");
218 }
219
220 /// Stops the spinner with a provided symbol and message
221 ///
222 /// # Examples
223 ///
224 /// Basic Usage:
225 ///
226 /// ```
227 /// use spinners::{Spinner, Spinners};
228 ///
229 /// let mut sp = Spinner::new(Spinners::Dots, "Loading things into memory...".into());
230 ///
231 /// sp.stop_and_persist("✔", "Finished loading things into memory!".into());
232 /// ```
233 pub fn stop_and_persist(&mut self, symbol: &str, msg: String) {
234 self.stop();
235 self.stream.stop(Some(&msg), Some(symbol)).expect("IO Error");
236 }
237
238 fn stop_inner(&mut self, stop_time: Instant, stop_symbol: Option<String>) {
239 self.sender
240 .send((stop_time, stop_symbol))
241 .expect("Could not stop spinner thread.");
242 self.join.take().unwrap().join().unwrap();
243 }
244}