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        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            // NOTE: this spawns a background thread which causes rendering (we don't want that)
90            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}