stati/bars/
custom.rs

1use std::time::Instant;
2
3mod default {
4    pub const FILLED: &str = "=";
5    pub const EMPTY: &str = "-";
6    pub const START: &str = "[";
7    pub const END: &str = "]";
8    pub const UNIT: &str = "its";
9}
10
11#[derive(Clone, Debug, Hash)]
12pub struct BarElements {
13    filled: String,
14    empty: String,
15    start: String,
16    end: String,
17    unit: String,
18}
19
20impl Default for BarElements {
21    fn default() -> Self {
22        use default::*;
23        Self {
24            filled: FILLED.to_string(),
25            empty: EMPTY.to_string(),
26            start: START.to_string(),
27            end: END.to_string(),
28            unit: UNIT.to_string(),
29        }
30    }
31}
32
33/// A Much more customiseable and advanced version of [`SimpleBar`].
34/// 
35/// To construct, use [`Builder`]
36/// 
37/// [`Builder`]: Builder
38/// [`SimpleBar`]: super::simple/*lmao*/::SimpleBar
39#[derive(Clone, Debug, Hash)]
40pub struct CustomBar {
41    job_name: String,
42    progress: usize,
43    max_hint: usize,
44    finished: bool,
45    last_iter: Instant,
46    elems: BarElements,
47}
48
49impl CustomBar {
50    pub fn set_name(&mut self, job_name: String) {
51        self.job_name = job_name;
52    }
53}
54
55impl crate::IsBar for CustomBar {
56    fn done(&mut self) {
57        self.finished = true;
58    }
59
60    #[must_use]
61    fn is_done(&self) -> bool {
62        self.finished
63    }
64
65    #[must_use]
66    fn display(&mut self) -> String {
67        let now = Instant::now();
68        let time_fmt = format!("{:.3}", 1f32 / (now - self.last_iter).as_secs_f32());
69        self.last_iter = now;
70
71        //TODO make this not use default
72        let width = crate::utils::term_width().unwrap_or(81) as usize;
73
74        let mut res = String::with_capacity(width);
75
76        let percentage = self.progress * 100 / self.max_hint;
77        let bar_len = width.checked_sub(
78            self.job_name.len() +
79            1 /* gap */ +
80            1 /* bar start */ +
81            /* bar would go here */
82            1 /* bar end */ +
83            1 /* gap */ +
84            5 /* precent len */ +
85            1 /*gap*/ +
86            time_fmt.len() /* time amnt*/+
87            1 /*gap*/ +
88            self.elems.unit.len() + 2 /* unit len (___/s) */
89        ).unwrap();
90        let bar_finished_len = (bar_len as f32 * percentage as f32 / 100.0) as isize;
91
92        res += "\r";
93        res += &self.job_name;
94        res += " ";
95        res += &self.elems.start;
96        for _ in 0..bar_finished_len {
97            res += &self.elems.filled;
98        }
99        for _ in bar_finished_len as usize..bar_len {
100            res += &self.elems.empty;
101        }
102        res += &self.elems.end;
103        //pad to 4 chars on left
104        res += &format!("{:>4}%", percentage);
105        res += " ";
106        res += &time_fmt;
107        res += " ";
108        res += &self.elems.unit;
109        res += "/s";
110
111        res
112    }
113
114    #[must_use]
115    fn close_method(&self) -> crate::isbar::BarCloseMethod {
116        crate::isbar::BarCloseMethod::LeaveBehind
117    }
118}
119
120impl crate::subsets::IteratorProgress for CustomBar {
121    fn set_progress(&mut self, progress: usize) {
122        self.progress = progress;
123    }
124
125    fn set_size_hint(&mut self, hint: usize) {
126        self.max_hint = hint;
127    }
128}
129
130/// Builder pattern builder for [`CustomBar`]
131/// 
132/// to create the builder, use [`new`] and to construct the bar use [`build`]
133/// 
134/// [`CustomBar`]: CustomBar
135/// [`new`]: Builder::new
136/// [`build`]: Builder::build
137pub struct Builder {
138    job_name: String,
139    hint: usize,
140    elems: BarElements,
141}
142
143impl Builder {
144    /// Create a new [`Builder`]
145    #[must_use]
146    pub fn new(name: impl ToString) -> Self {
147        Self {
148            job_name: name.to_string(),
149            hint: 100,
150            elems: BarElements {
151                ..Default::default()
152            },
153        }
154    }
155
156    /// Sets the size hint
157    #[must_use]
158    pub fn hint(mut self, hint: usize) -> Self {
159        self.hint = hint;
160        self
161    }
162
163    /// Set the bar element customization
164    #[must_use]
165    pub fn elems(mut self, elems: BarElements) -> Self {
166        self.elems = elems;
167        self
168    }
169
170    /// Set the string for the filled bar section (should be one charecter in length, is repeated)
171    #[must_use]
172    pub fn filled(mut self, v: impl ToString) -> Self {
173        self.elems.filled = v.to_string();
174        self
175    }
176
177    /// Set the string for the empty bar section (should be one charecter in length, is repeated)
178    #[must_use]
179    pub fn empty(mut self, v: impl ToString) -> Self {
180        self.elems.empty = v.to_string();
181        self
182    }
183
184    /// Set the start string for the bar section
185    #[must_use]
186    pub fn start(mut self, v: impl ToString) -> Self {
187        self.elems.start = v.to_string();
188        self
189    }
190
191    /// Set the end string for the bar section
192    #[must_use]
193    pub fn end(mut self, v: impl ToString) -> Self {
194        self.elems.end = v.to_string();
195        self
196    }
197
198    /// Sets the unit in ___/sec for the progress bar
199    #[must_use]
200    pub fn unit(mut self, v: impl ToString) -> Self {
201        self.elems.unit = v.to_string();
202        self
203    }
204
205    /// Build the [`CustomBar`]
206    #[must_use]
207    pub fn build(self) -> CustomBar {
208        CustomBar {
209            job_name: self.job_name,
210            max_hint: self.hint,
211            elems: self.elems,
212            progress: 0,
213            last_iter: Instant::now(),
214            finished: false,
215        }
216    }
217}