1use std::{borrow::Cow, sync::Arc, time::Duration};
2
3use crate::{config::Config, logging::LoggingState};
4
5mod private {
6 pub trait HasProgress {}
7}
8
9impl private::HasProgress for () {}
10impl private::HasProgress for MultiProgress {}
11impl private::HasProgress for ProgressBar {}
12
13pub trait HasProgress: private::HasProgress {}
14impl<P: private::HasProgress> HasProgress for P {}
15
16#[derive(Clone)]
17pub struct Progress<T: HasProgress>(ProgressImpl<T>);
18
19#[derive(Clone)]
20enum ProgressImpl<T: HasProgress> {
21 NoProgress,
22 Progress(T),
23}
24
25impl<T> Progress<T>
26where
27 T: HasProgress,
28{
29 pub fn new(progress: T, config: &Config) -> Progress<T> {
30 Progress(if config.no_progress() {
31 ProgressImpl::NoProgress
32 } else {
33 ProgressImpl::Progress(progress)
34 })
35 }
36
37 pub fn no_progress() -> Progress<T> {
38 Progress(ProgressImpl::NoProgress)
39 }
40
41 pub fn map<F, R>(&self, callback: F) -> Progress<R>
42 where
43 F: FnOnce(&T) -> R,
44 R: HasProgress,
45 {
46 Progress(if let ProgressImpl::Progress(progress) = &self.0 {
47 ProgressImpl::Progress(callback(progress))
48 } else {
49 ProgressImpl::NoProgress
50 })
51 }
52}
53
54pub struct MultiProgress(indicatif::MultiProgress);
56pub struct ProgressBar(indicatif::ProgressBar);
57
58impl MultiProgress {
59 pub fn new(config: &Config) -> Progress<Self> {
60 Progress::new(Self(indicatif::MultiProgress::new()), config)
61 }
62
63 pub fn new_arc(config: &Config) -> Arc<Progress<MultiProgress>> {
64 Arc::new(MultiProgress::new(config))
65 }
66
67 pub fn add(&self, bar: ProgressBar) -> ProgressBar {
68 ProgressBar(self.0.insert_from_back(0, bar.0))
69 }
70
71 pub fn new_bar(&self) -> ProgressBar {
72 self.add(ProgressBar::new())
73 }
74
75 pub fn suspend<F, R>(&self, callback: F) -> R
76 where
77 F: FnOnce() -> R,
78 {
79 self.0.suspend(callback)
80 }
81}
82
83impl ProgressBar {
84 pub fn new() -> Self {
85 let bar =
86 indicatif::ProgressBar::new_spinner().with_finish(indicatif::ProgressFinish::AndClear);
87
88 if let LoggingState::Disabled = crate::logging::state() {
89 bar.enable_steady_tick(Duration::from_millis(100));
91 }
92
93 Self(bar)
94 }
95
96 pub fn into_raw(self) -> indicatif::ProgressBar {
97 self.0
98 }
99
100 pub fn set_message<M>(&self, message: M)
101 where
102 M: Into<Cow<'static, str>>,
103 {
104 let msg: Cow<'static, str> = message.into();
105 match crate::logging::state() {
106 crate::logging::LoggingState::Enabled => {
107 crate::logging::push(
108 crate::logging::LogLevel::Info,
109 msg.clone().into_owned(),
110 Some("progress".to_string()),
111 );
112 }
113 crate::logging::LoggingState::Disabled => {
114 self.0.set_message(msg);
115 }
116 }
117 }
118
119 pub fn set_position(&self, position: u64) {
120 self.0.set_position(position)
121 }
122
123 pub fn position(&self) -> u64 {
124 self.0.position()
125 }
126
127 pub fn println<M>(&self, message: M)
128 where
129 M: AsRef<str>,
130 {
131 match crate::logging::state() {
132 crate::logging::LoggingState::Enabled => {
133 crate::logging::push(
134 crate::logging::LogLevel::Info,
135 message.as_ref().to_string(),
136 Some("progress".to_string()),
137 );
138 }
139 crate::logging::LoggingState::Disabled => {
140 self.0.println(message.as_ref());
141 }
142 }
143 }
144
145 pub fn finish_with_message<M>(&self, message: M)
146 where
147 M: Into<Cow<'static, str>>,
148 {
149 let msg: Cow<'static, str> = message.into();
150 match crate::logging::state() {
151 crate::logging::LoggingState::Enabled => {
152 crate::logging::push(
153 crate::logging::LogLevel::Info,
154 msg.clone().into_owned(),
155 Some("progress".to_string()),
156 );
157 }
158 crate::logging::LoggingState::Disabled => {
159 self.0.finish_with_message(msg);
160 }
161 }
162 }
163
164 pub fn finish_and_clear(&self) {
165 self.0.finish_and_clear()
166 }
167}
168
169impl Default for ProgressBar {
170 fn default() -> Self {
171 Self::new()
172 }
173}
174
175impl From<String> for ProgressBar {
176 fn from(message: String) -> Self {
177 Self(Self::new().0.with_message(message))
178 }
179}
180
181impl From<u64> for ProgressBar {
182 fn from(position: u64) -> Self {
183 let new = Self::new();
184 new.set_position(position);
185
186 new
187 }
188}