Skip to main content

lux_lib/
progress.rs

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
54// WARNING: Don't implement `Clone` for this.
55pub 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            // NOTE: this spawns a background thread which causes rendering (we don't want that)
94            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}