1use std::{
41 io::{stderr, stdout, Result, Write},
42 thread,
43 time::Duration,
44};
45
46#[cfg(feature = "end")]
47mod utils;
48
49const PATTERN: &str = "⠁⠁⠂⠂⠄⠄⡀⡀⡀⠠⠠⠐⠐⠈";
51
52fn clear_stdout() -> Result<()> {
53 stdout().flush()?;
54 Ok(())
55}
56
57fn write_to_stdout(buf: String) -> Result<()> {
58 let mut output = stdout();
59 output.write(buf.as_bytes())?;
60 output.flush()?;
61 Ok(())
62}
63
64fn write_to_stderr(buf: String) -> Result<()> {
65 let mut output = stderr();
66 output.write(buf.as_bytes())?;
67 output.flush()?;
68 Ok(())
69}
70
71pub trait Progress<T> {
73 fn new() -> T;
74 fn to_stderr(&mut self) -> T;
75 fn write(&self, buf: String) -> Result<()>;
76 #[cfg(feature = "end")]
77 fn write_done(&self) -> Result<()>;
78 fn clear(&self) -> Result<()>;
79}
80
81#[derive(Clone)]
83pub struct Infinite {
84 msg: String,
85 marker_position: u8,
86 step: u8,
87 delay: Duration,
88 write_fn: fn(String) -> Result<()>,
89 clear_fn: fn() -> Result<()>,
90 rolling: bool,
91 done: bool,
92}
93
94impl Default for Infinite {
95 fn default() -> Infinite {
96 Infinite {
97 step: 1,
98 delay: Duration::from_millis(100),
99 msg: "".to_owned(),
100 marker_position: 0,
101 write_fn: write_to_stdout,
102 clear_fn: clear_stdout,
103 rolling: false,
104 done: false,
105 }
106 }
107}
108
109impl Progress<Infinite> for Infinite {
110 fn new() -> Infinite {
111 Infinite {
112 ..Default::default()
113 }
114 }
115
116 fn to_stderr(&mut self) -> Infinite {
117 self.write_fn = write_to_stderr;
118 self.clone()
119 }
120
121 fn write(&self, buf: String) -> Result<()> {
122 (self.write_fn)(buf)
123 }
124 #[cfg(feature = "end")]
125 fn write_done(&self) -> Result<()> {
126 let buf = utils::print_green("done\n");
127 (self.write_fn)(buf)
128 }
129 fn clear(&self) -> Result<()> {
130 (self.clear_fn)()?;
131 Ok(())
132 }
133}
134
135impl Infinite {
136 pub fn set_msg(&mut self, msg: &str) {
137 self.msg = msg.to_owned()
138 }
139 pub fn set_delay(&mut self, d: Duration) {
140 self.delay = d
141 }
142 pub fn set_done(&mut self, done: bool) {
143 self.done = done
144 }
145
146 pub fn get_msg(&self) -> &str {
147 self.msg.as_ref()
148 }
149 pub fn get_delay(&self) -> Duration {
150 self.delay
151 }
152
153 pub fn stop(&mut self) -> Result<()> {
154 self.rolling = false;
155 self.clear()?;
156 self.render_end()?;
157 Ok(())
158 }
159 pub fn start<'a>(&'a mut self) -> Result<String> {
160 self.rolling = true;
161 let mut bar = self.clone();
162 let sleep = self.delay;
163 let thread_name = "rolling";
164 thread::Builder::new()
165 .name(thread_name.clone().into())
166 .spawn(move || loop {
167 bar.render().unwrap();
168 thread::sleep(sleep);
169 })?;
170 Ok(thread_name.into())
171 }
172
173 #[cfg(feature = "end")]
174 pub fn render_end(&mut self) -> Result<()> {
175 if self.done {
176 self.write_done()
177 } else {
178 self.write("\r".into())
179 }
180 }
181 #[cfg(not(feature = "end"))]
182 pub fn render_end(&mut self) -> Result<()> {
183 self.write("\r".into())
184 }
185 pub fn render(&mut self) -> Result<()> {
186 if self.marker_position == 0 {
187 self.marker_position = 0;
188 self.step = 1;
189 } else if self.marker_position == 12 {
190 self.marker_position = 0;
191 self.step = 1;
192 }
193 self.marker_position = self.marker_position + self.step;
194
195 let bar = PATTERN
196 .chars()
197 .nth(usize::from(self.marker_position))
198 .expect("out of bounds");
199
200 self.write(format!(
201 "\r{msg} {bar}{bar}{bar} ",
202 msg = self.msg,
203 bar = bar
204 ))?;
205 Ok(())
206 }
207}