Skip to content

Commit 8941f4b

Browse files
committed
feat: Introduce the object-safe RawProgress trait.
It's automatically implemented for `Progress` and allows for more flexible use of progress particularly in leaf nodes. This is useful if a function needs to take multiple types of progress as it is called from different places in the same function. Without dyn-traits, it's not possible to make such call.
1 parent 41ad0a4 commit 8941f4b

File tree

4 files changed

+233
-3
lines changed

4 files changed

+233
-3
lines changed

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ pub mod messages;
6464
pub mod progress;
6565

6666
mod traits;
67-
pub use traits::{Progress, Root, WeakRoot};
67+
pub use traits::{Progress, RawProgress, Root, WeakRoot};
6868

6969
mod throughput;
7070
pub use crate::throughput::Throughput;

src/traits.rs

Lines changed: 220 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::time::Instant;
22

33
use crate::{messages::MessageLevel, progress, progress::Id, Unit};
44

5-
/// A trait for describing hierarchical process.
5+
/// A trait for describing hierarchical progress.
66
pub trait Progress: Send {
77
/// The type of progress returned by [`add_child()`][Progress::add_child()].
88
type SubProgress: Progress;
@@ -154,6 +154,146 @@ pub trait Progress: Send {
154154
}
155155
}
156156

157+
/// A trait for describing non-hierarchical progress.
158+
///
159+
/// It differs by not being able to add child progress dynamically, but in turn is object safe. It's recommended to
160+
/// use this trait whenever there is no need to add child progress, at the leaf of a computation.
161+
// NOTE: keep this in-sync with `Progress`.
162+
pub trait RawProgress: Send {
163+
/// Initialize the Item for receiving progress information.
164+
///
165+
/// If `max` is `Some(…)`, it will be treated as upper bound. When progress is [set(…)](./struct.Item.html#method.set)
166+
/// it should not exceed the given maximum.
167+
/// If `max` is `None`, the progress is unbounded. Use this if the amount of work cannot accurately
168+
/// be determined in advance.
169+
///
170+
/// If `unit` is `Some(…)`, it is used for display purposes only. See `prodash::Unit` for more information.
171+
///
172+
/// If both `unit` and `max` are `None`, the item will be reset to be equivalent to 'uninitialized'.
173+
///
174+
/// If this method is never called, this `Progress` instance will serve as organizational unit, useful to add more structure
175+
/// to the progress tree (e.g. a headline).
176+
///
177+
/// **Note** that this method can be called multiple times, changing the bounded-ness and unit at will.
178+
fn init(&mut self, max: Option<progress::Step>, unit: Option<Unit>);
179+
180+
/// Set the current progress to the given `step`. The cost of this call is negligible,
181+
/// making manual throttling *not* necessary.
182+
///
183+
/// **Note**: that this call has no effect unless `init(…)` was called before.
184+
fn set(&mut self, step: progress::Step);
185+
186+
/// Returns the (cloned) unit associated with this Progress
187+
fn unit(&self) -> Option<Unit> {
188+
None
189+
}
190+
191+
/// Returns the maximum about of items we expect, as provided with the `init(…)` call
192+
fn max(&self) -> Option<progress::Step> {
193+
None
194+
}
195+
196+
/// Set the maximum value to `max` and return the old maximum value.
197+
fn set_max(&mut self, _max: Option<progress::Step>) -> Option<progress::Step> {
198+
None
199+
}
200+
201+
/// Returns the current step, as controlled by `inc*(…)` calls
202+
fn step(&self) -> progress::Step;
203+
204+
/// Increment the current progress to the given `step`.
205+
/// The cost of this call is negligible, making manual throttling *not* necessary.
206+
fn inc_by(&mut self, step: progress::Step);
207+
208+
/// Increment the current progress to the given 1. The cost of this call is negligible,
209+
/// making manual throttling *not* necessary.
210+
fn inc(&mut self) {
211+
self.inc_by(1)
212+
}
213+
214+
/// Set the name of the instance, altering the value given when crating it with `add_child(…)`
215+
/// The progress is allowed to discard it.
216+
fn set_name(&mut self, name: String);
217+
218+
/// Get the name of the instance as given when creating it with `add_child(…)`
219+
/// The progress is allowed to not be named, thus there is no guarantee that a previously set names 'sticks'.
220+
fn name(&self) -> Option<String>;
221+
222+
/// Get a stable identifier for the progress instance.
223+
/// Note that it could be [unknown][crate::progress::UNKNOWN].
224+
fn id(&self) -> Id;
225+
226+
/// Create a `message` of the given `level` and store it with the progress tree.
227+
///
228+
/// Use this to provide additional,human-readable information about the progress
229+
/// made, including indicating success or failure.
230+
fn message(&mut self, level: MessageLevel, message: String);
231+
232+
/// If available, return an atomic counter for direct access to the underlying state.
233+
///
234+
/// This is useful if multiple threads want to access the same progress, without the need
235+
/// for provide each their own progress and aggregating the result.
236+
fn counter(&self) -> Option<StepShared> {
237+
None
238+
}
239+
240+
/// Create a message providing additional information about the progress thus far.
241+
fn info(&mut self, message: String) {
242+
self.message(MessageLevel::Info, message)
243+
}
244+
/// Create a message indicating the task is done successfully
245+
fn done(&mut self, message: String) {
246+
self.message(MessageLevel::Success, message)
247+
}
248+
/// Create a message indicating the task failed
249+
fn fail(&mut self, message: String) {
250+
self.message(MessageLevel::Failure, message)
251+
}
252+
/// A shorthand to print throughput information
253+
fn show_throughput(&mut self, start: Instant) {
254+
let step = self.step();
255+
match self.unit() {
256+
Some(unit) => self.show_throughput_with(start, step, unit, MessageLevel::Info),
257+
None => {
258+
let elapsed = start.elapsed().as_secs_f32();
259+
let steps_per_second = (step as f32 / elapsed) as progress::Step;
260+
self.info(format!(
261+
"done {} items in {:.02}s ({} items/s)",
262+
step, elapsed, steps_per_second
263+
))
264+
}
265+
};
266+
}
267+
268+
/// A shorthand to print throughput information, with the given step and unit, and message level.
269+
fn show_throughput_with(&mut self, start: Instant, step: progress::Step, unit: Unit, level: MessageLevel) {
270+
use std::fmt::Write;
271+
let elapsed = start.elapsed().as_secs_f32();
272+
let steps_per_second = (step as f32 / elapsed) as progress::Step;
273+
let mut buf = String::with_capacity(128);
274+
let unit = unit.as_display_value();
275+
let push_unit = |buf: &mut String| {
276+
buf.push(' ');
277+
let len_before_unit = buf.len();
278+
unit.display_unit(buf, step).ok();
279+
if buf.len() == len_before_unit {
280+
buf.pop();
281+
}
282+
};
283+
284+
buf.push_str("done ");
285+
unit.display_current_value(&mut buf, step, None).ok();
286+
push_unit(&mut buf);
287+
288+
buf.write_fmt(format_args!(" in {:.02}s (", elapsed)).ok();
289+
unit.display_current_value(&mut buf, steps_per_second, None).ok();
290+
push_unit(&mut buf);
291+
buf.push_str("/s)");
292+
293+
self.message(level, buf);
294+
}
295+
}
296+
157297
use crate::{
158298
messages::{Message, MessageCopyState},
159299
progress::StepShared,
@@ -205,12 +345,90 @@ mod impls {
205345
time::Instant,
206346
};
207347

348+
use crate::traits::RawProgress;
208349
use crate::{
209350
messages::MessageLevel,
210351
progress::{Id, Step, StepShared},
211352
Progress, Unit,
212353
};
213354

355+
impl<T> RawProgress for T
356+
where
357+
T: Progress,
358+
{
359+
fn init(&mut self, max: Option<Step>, unit: Option<Unit>) {
360+
<T as Progress>::init(self, max, unit)
361+
}
362+
363+
fn set(&mut self, step: Step) {
364+
<T as Progress>::set(self, step)
365+
}
366+
367+
fn unit(&self) -> Option<Unit> {
368+
<T as Progress>::unit(self)
369+
}
370+
371+
fn max(&self) -> Option<Step> {
372+
<T as Progress>::max(self)
373+
}
374+
375+
fn set_max(&mut self, max: Option<Step>) -> Option<Step> {
376+
<T as Progress>::set_max(self, max)
377+
}
378+
379+
fn step(&self) -> Step {
380+
<T as Progress>::step(self)
381+
}
382+
383+
fn inc_by(&mut self, step: Step) {
384+
<T as Progress>::inc_by(self, step)
385+
}
386+
387+
fn inc(&mut self) {
388+
<T as Progress>::inc(self)
389+
}
390+
391+
fn set_name(&mut self, name: String) {
392+
<T as Progress>::set_name(self, name)
393+
}
394+
395+
fn name(&self) -> Option<String> {
396+
<T as Progress>::name(self)
397+
}
398+
399+
fn id(&self) -> Id {
400+
<T as Progress>::id(self)
401+
}
402+
403+
fn message(&mut self, level: MessageLevel, message: String) {
404+
<T as Progress>::message(self, level, message)
405+
}
406+
407+
fn counter(&self) -> Option<StepShared> {
408+
<T as Progress>::counter(self)
409+
}
410+
411+
fn info(&mut self, message: String) {
412+
<T as Progress>::info(self, message)
413+
}
414+
415+
fn done(&mut self, message: String) {
416+
<T as Progress>::done(self, message)
417+
}
418+
419+
fn fail(&mut self, message: String) {
420+
<T as Progress>::fail(self, message)
421+
}
422+
423+
fn show_throughput(&mut self, start: Instant) {
424+
<T as Progress>::show_throughput(self, start)
425+
}
426+
427+
fn show_throughput_with(&mut self, start: Instant, step: Step, unit: Unit, level: MessageLevel) {
428+
<T as Progress>::show_throughput_with(self, start, step, unit, level)
429+
}
430+
}
431+
214432
impl<'a, T> Progress for &'a mut T
215433
where
216434
T: Progress,
@@ -266,7 +484,7 @@ mod impls {
266484
}
267485

268486
fn id(&self) -> Id {
269-
todo!()
487+
self.deref().id()
270488
}
271489

272490
fn message(&mut self, level: MessageLevel, message: impl Into<String>) {

tests/prodash.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
mod progress;
2+
mod raw_progress;
23
mod unit;

tests/raw_progress/mod.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
use prodash::RawProgress;
2+
3+
#[test]
4+
fn dyn_safe() {
5+
fn needs_dyn(_p: &mut dyn RawProgress) {}
6+
let root = prodash::tree::Root::new();
7+
let mut child = root.add_child("hello");
8+
needs_dyn(&mut child);
9+
let mut child_of_child = child.add_child("there");
10+
needs_dyn(&mut child_of_child);
11+
}

0 commit comments

Comments
 (0)