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 if let LoggingState::Disabled = crate::logging::state() {
69 ProgressBar(self.0.insert_from_back(0, bar.0))
70 } else {
71 bar
72 }
73 }
74
75 pub fn new_bar(&self) -> ProgressBar {
76 self.add(ProgressBar::new())
77 }
78
79 pub fn suspend<F, R>(&self, callback: F) -> R
80 where
81 F: FnOnce() -> R,
82 {
83 self.0.suspend(callback)
84 }
85}
86
87impl ProgressBar {
88 pub fn new() -> Self {
89 let bar =
90 indicatif::ProgressBar::new_spinner().with_finish(indicatif::ProgressFinish::AndClear);
91
92 if let LoggingState::Disabled = crate::logging::state() {
93 bar.enable_steady_tick(Duration::from_millis(100));
95 }
96
97 Self(bar)
98 }
99
100 pub fn into_raw(self) -> indicatif::ProgressBar {
101 self.0
102 }
103
104 pub fn set_message<M>(&self, message: M)
105 where
106 M: Into<Cow<'static, str>>,
107 {
108 let msg: Cow<'static, str> = message.into();
109 match crate::logging::state() {
110 crate::logging::LoggingState::Enabled => {
111 crate::logging::push(
112 crate::logging::LogLevel::Info,
113 msg.clone().into_owned(),
114 Some("progress".to_string()),
115 );
116 }
117 crate::logging::LoggingState::Disabled => {
118 self.0.set_message(msg);
119 }
120 }
121 }
122
123 pub fn set_position(&self, position: u64) {
124 self.0.set_position(position)
125 }
126
127 pub fn position(&self) -> u64 {
128 self.0.position()
129 }
130
131 pub fn println<M>(&self, message: M)
132 where
133 M: AsRef<str>,
134 {
135 match crate::logging::state() {
136 crate::logging::LoggingState::Enabled => {
137 crate::logging::push(
138 crate::logging::LogLevel::Info,
139 message.as_ref().to_string(),
140 Some("progress".to_string()),
141 );
142 }
143 crate::logging::LoggingState::Disabled => {
144 self.0.println(message.as_ref());
145 }
146 }
147 }
148
149 pub fn finish_with_message<M>(&self, message: M)
150 where
151 M: Into<Cow<'static, str>>,
152 {
153 let msg: Cow<'static, str> = message.into();
154 match crate::logging::state() {
155 crate::logging::LoggingState::Enabled => {
156 crate::logging::push(
157 crate::logging::LogLevel::Info,
158 msg.clone().into_owned(),
159 Some("progress".to_string()),
160 );
161 }
162 crate::logging::LoggingState::Disabled => {
163 self.0.finish_with_message(msg);
164 }
165 }
166 }
167
168 pub fn finish_and_clear(&self) {
169 if let LoggingState::Disabled = crate::logging::state() {
170 self.0.finish_and_clear()
171 }
172 }
173}
174
175impl Default for ProgressBar {
176 fn default() -> Self {
177 Self::new()
178 }
179}
180
181impl From<String> for ProgressBar {
182 fn from(message: String) -> Self {
183 let bar = Self::new();
184 bar.set_message(message);
185 bar
186 }
187}
188
189impl From<u64> for ProgressBar {
190 fn from(position: u64) -> Self {
191 let new = Self::new();
192 new.set_position(position);
193
194 new
195 }
196}