Skip to content

Commit

Permalink
feat: identify each progress item with Id using add_child_with_id().
Browse files Browse the repository at this point in the history
An `Id` is four bytes like b"TREE" that are stable and
identify progress items (as created by `add_child(…)` within
a function call.

Callers may use this knowledge to pick specific progress items
for consumption, instead of trying to rely on identifying tasks
by name which may change.

The identifier can also be queried with `Progress::id()`, even
though it could be `prodash::progress::UNKNOWN` if the progress
item wasn't created with `add_child_with_id()`.
  • Loading branch information
Byron committed Nov 23, 2022
1 parent d7fcf5b commit c332a6f
Show file tree
Hide file tree
Showing 9 changed files with 153 additions and 23 deletions.
16 changes: 8 additions & 8 deletions examples/units.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@ fn main() -> Result {
}

fn work_for_a_long_time_blocking(root: Arc<Tree>) {
let mut bytes = root.add_child("download unknown");
let mut bytes = root.add_child_with_id("download unknown", *b"DLUK");
bytes.init(
None,
Some(unit::dynamic_and_mode(
unit::Bytes,
unit::display::Mode::with_throughput(),
)),
);
let mut bytes_max = root.add_child("download");
let mut bytes_max = root.add_child_with_id("download", *b"DLKN");
bytes_max.init(
Some(100_000_000),
Some(unit::dynamic_and_mode(
Expand All @@ -43,9 +43,9 @@ fn work_for_a_long_time_blocking(root: Arc<Tree>) {
)),
);

let mut duration = root.add_child("duration unknown");
let mut duration = root.add_child_with_id("duration unknown", *b"DRUK");
duration.init(None, Some(unit::dynamic(unit::Duration)));
let mut duration_max = root.add_child("duration");
let mut duration_max = root.add_child_with_id("duration", *b"DRKN");
duration_max.init(
Some(60 * 60 * 24),
Some(unit::dynamic_and_mode(
Expand All @@ -59,15 +59,15 @@ fn work_for_a_long_time_blocking(root: Arc<Tree>) {
f.with_decimals(decimals);
f
}
let mut human_count = root.add_child("item count unknown");
let mut human_count = root.add_child_with_id("item count unknown", *b"ITUK");
human_count.init(
None,
Some(unit::dynamic_and_mode(
unit::Human::new(formatter(0), "items"),
unit::display::Mode::with_throughput(),
)),
);
let mut human_count_max = root.add_child("item count");
let mut human_count_max = root.add_child_with_id("item count", *b"ITKN");
human_count_max.init(
Some(7_542_241),
Some(unit::dynamic_and_mode(
Expand All @@ -76,15 +76,15 @@ fn work_for_a_long_time_blocking(root: Arc<Tree>) {
)),
);

let mut steps = root.add_child("steps to take unknown");
let mut steps = root.add_child_with_id("steps to take unknown", *b"STUK");
steps.init(
None,
Some(unit::dynamic_and_mode(
unit::Range::new("steps"),
unit::display::Mode::with_throughput(),
)),
);
let mut steps_max = root.add_child("steps to take");
let mut steps_max = root.add_child_with_id("steps to take", *b"STKN");
steps_max.init(
Some(100),
Some(unit::dynamic_and_mode(
Expand Down
13 changes: 12 additions & 1 deletion src/progress/log.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::progress::{Step, StepShared};
use crate::progress::{Id, Step, StepShared};
use crate::{messages::MessageLevel, Progress, Unit};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
Expand All @@ -10,6 +10,7 @@ use std::time::Duration;
/// to see if progress information should actually be emitted.
pub struct Log {
name: String,
id: Id,
max: Option<usize>,
unit: Option<Unit>,
step: usize,
Expand Down Expand Up @@ -37,6 +38,7 @@ impl Log {
});
Log {
name: name.into(),
id: crate::progress::UNKNOWN,
current_level: 0,
max_level: max_level.unwrap_or(usize::MAX),
max: None,
Expand All @@ -51,8 +53,13 @@ impl Progress for Log {
type SubProgress = Log;

fn add_child(&mut self, name: impl Into<String>) -> Self::SubProgress {
self.add_child_with_id(name, crate::progress::UNKNOWN)
}

fn add_child_with_id(&mut self, name: impl Into<String>, id: Id) -> Self::SubProgress {
Log {
name: format!("{}{}{}", self.name, SEP, Into::<String>::into(name)),
id,
current_level: self.current_level + 1,
max_level: self.max_level,
step: 0,
Expand Down Expand Up @@ -117,6 +124,10 @@ impl Progress for Log {
self.name.split(SEP).nth(1).map(ToOwned::to_owned)
}

fn id(&self) -> Id {
self.id
}

fn message(&mut self, level: MessageLevel, message: impl Into<String>) {
let message: String = message.into();
match level {
Expand Down
15 changes: 15 additions & 0 deletions src/progress/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,18 @@ pub use self::log::Log;

pub use utils::{Discard, DoOrDiscard, Either, ThroughputOnDrop};

/// Four bytes of function-local unique and stable identifier for each item added as progress,
/// like b"TREE" or b"FILE".
///
/// Note that uniqueness only relates to one particular method call where those interested in its progress
/// may assume certain stable ids to look for when selecting specific bits of progress to process.
pub type Id = [u8; 4];

/// The default Id to use if there is no need for an id.
///
/// This is the default unless applications wish to make themselves more introspectable.
pub const UNKNOWN: Id = *b"\0\0\0\0";

/// The amount of steps a progress can make
pub type Step = usize;

Expand Down Expand Up @@ -72,6 +84,9 @@ impl Value {
pub struct Task {
/// The name of the `Item` or task.
pub name: String,
/// The stable identifier of this task.
/// Useful for selecting specific tasks out of a set of them.
pub id: Id,
/// The progress itself, unless this value belongs to an `Item` serving as organizational unit.
pub progress: Option<Value>,
}
37 changes: 36 additions & 1 deletion src/progress/utils.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{messages::MessageLevel, Progress, Unit};
use crate::{messages::MessageLevel, progress::Id, Progress, Unit};

/// An implementation of [`Progress`] which discards all calls.
pub struct Discard;
Expand All @@ -10,6 +10,10 @@ impl Progress for Discard {
Discard
}

fn add_child_with_id(&mut self, _name: impl Into<String>, _id: Id) -> Self::SubProgress {
Discard
}

fn init(&mut self, _max: Option<usize>, _unit: Option<Unit>) {}

fn set(&mut self, _step: usize) {}
Expand All @@ -30,6 +34,10 @@ impl Progress for Discard {
None
}

fn id(&self) -> Id {
crate::progress::UNKNOWN
}

fn message(&mut self, _level: MessageLevel, _message: impl Into<String>) {}

fn counter(&self) -> Option<StepShared> {
Expand Down Expand Up @@ -62,6 +70,13 @@ where
}
}

fn add_child_with_id(&mut self, name: impl Into<String>, id: Id) -> Self::SubProgress {
match self {
Either::Left(l) => Either::Left(l.add_child_with_id(name, id)),
Either::Right(r) => Either::Right(r.add_child_with_id(name, id)),
}
}

fn init(&mut self, max: Option<usize>, unit: Option<Unit>) {
match self {
Either::Left(l) => l.init(max, unit),
Expand Down Expand Up @@ -125,6 +140,10 @@ where
}
}

fn id(&self) -> Id {
todo!()
}

fn message(&mut self, level: MessageLevel, message: impl Into<String>) {
match self {
Either::Left(l) => l.message(level, message),
Expand Down Expand Up @@ -184,6 +203,10 @@ where
DoOrDiscard(self.0.add_child(name))
}

fn add_child_with_id(&mut self, name: impl Into<String>, id: Id) -> Self::SubProgress {
DoOrDiscard(self.0.add_child_with_id(name, id))
}

fn init(&mut self, max: Option<usize>, unit: Option<Unit>) {
self.0.init(max, unit)
}
Expand Down Expand Up @@ -220,6 +243,10 @@ where
self.0.name()
}

fn id(&self) -> Id {
self.0.id()
}

fn message(&mut self, level: MessageLevel, message: impl Into<String>) {
self.0.message(level, message)
}
Expand Down Expand Up @@ -249,6 +276,10 @@ impl<T: Progress> Progress for ThroughputOnDrop<T> {
self.0.add_child(name)
}

fn add_child_with_id(&mut self, name: impl Into<String>, id: Id) -> Self::SubProgress {
self.0.add_child_with_id(name, id)
}

fn init(&mut self, max: Option<usize>, unit: Option<Unit>) {
self.0.init(max, unit)
}
Expand Down Expand Up @@ -285,6 +316,10 @@ impl<T: Progress> Progress for ThroughputOnDrop<T> {
self.0.name()
}

fn id(&self) -> Id {
self.0.id()
}

fn message(&mut self, level: MessageLevel, message: impl Into<String>) {
self.0.message(level, message)
}
Expand Down
15 changes: 14 additions & 1 deletion src/render/tui/draw/progress.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,20 @@ pub fn draw_progress(
None => state,
});

for (line, (entry_index, (key, Task { progress, name: title }))) in entries
for (
line,
(
entry_index,
(
key,
Task {
progress,
name: title,
id: _,
},
),
),
) in entries
.iter()
.enumerate()
.skip(offset as usize)
Expand Down
14 changes: 8 additions & 6 deletions src/render/tui/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,17 @@
* ```should_panic
* # fn main() -> Result<(), Box<dyn std::error::Error>> {
* use futures::task::{LocalSpawnExt, SpawnExt};
* use prodash::tui::ticker;
* use prodash::render::tui::ticker;
* use prodash::Root;
* // obtain a progress tree
* let root = prodash::Tree::new();
* // Configure the gui, provide it with a handle to the ever-changing tree
* let render_fut = prodash::tui::render(
* root.clone(),
* prodash::tui::Options {
* let render_fut = prodash::render::tui::render(
* std::io::stdout(),
* root.downgrade(),
* prodash::render::tui::Options {
* title: "minimal example".into(),
* ..prodash::tui::Options::default()
* ..prodash::render::tui::Options::default()
* }
* )?;
* // As it runs forever, we want a way to stop it.
Expand All @@ -30,7 +32,7 @@
* use futures::StreamExt;
* let mut progress = root.add_child("task");
* async move {
* progress.init(None, Some("items"));
* progress.init(None, None);
* let mut count = 0;
* let mut ticks = ticker(std::time::Duration::from_millis(100));
* while let Some(_) = ticks.next().await {
Expand Down
26 changes: 23 additions & 3 deletions src/traits.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
use crate::{messages::MessageLevel, progress, Unit};
use crate::{messages::MessageLevel, progress, progress::Id, Unit};
use std::time::Instant;

/// A trait for describing hierarchical process.
pub trait Progress: Send {
/// The type of progress returned by [`add_child()`][Progress::add_child()].
type SubProgress: Progress;

/// Adds a new child, whose parent is this instance, with the given name.
/// Adds a new child, whose parent is this instance, with the given `name`.
///
/// This will make the child progress to appear contained in the parent progress.
/// Note that such progress does not have a stable identifier, which can be added
/// with [`add_child_with_id()`][Progress::add_child_with_id()] if desired.
fn add_child(&mut self, name: impl Into<String>) -> Self::SubProgress;

/// Adds a new child, whose parent is this instance, with the given `name` and `id`.
///
/// This will make the child progress to appear contained in the parent progress, and it can be identified
/// using `id`.
fn add_child_with_id(&mut self, name: impl Into<String>, id: Id) -> Self::SubProgress;

/// Initialize the Item for receiving progress information.
///
/// If `max` is `Some(…)`, it will be treated as upper bound. When progress is [set(…)](./struct.Item.html#method.set)
Expand Down Expand Up @@ -70,6 +78,10 @@ pub trait Progress: Send {
/// The progress is allowed to not be named, thus there is no guarantee that a previously set names 'sticks'.
fn name(&self) -> Option<String>;

/// Get a stable identifier for the progress instance.
/// Note that it could be [unknown][crate::progress::UNKNOWN].
fn id(&self) -> Id;

/// Create a `message` of the given `level` and store it with the progress tree.
///
/// Use this to provide additional,human-readable information about the progress
Expand Down Expand Up @@ -186,7 +198,7 @@ pub trait Root {

mod impls {
use crate::messages::MessageLevel;
use crate::progress::{Step, StepShared};
use crate::progress::{Id, Step, StepShared};
use crate::{Progress, Unit};
use std::ops::{Deref, DerefMut};
use std::time::Instant;
Expand All @@ -201,6 +213,10 @@ mod impls {
self.deref_mut().add_child(name)
}

fn add_child_with_id(&mut self, name: impl Into<String>, id: Id) -> Self::SubProgress {
self.deref_mut().add_child_with_id(name, id)
}

fn init(&mut self, max: Option<Step>, unit: Option<Unit>) {
self.deref_mut().init(max, unit)
}
Expand Down Expand Up @@ -241,6 +257,10 @@ mod impls {
self.deref().name()
}

fn id(&self) -> Id {
todo!()
}

fn message(&mut self, level: MessageLevel, message: impl Into<String>) {
self.deref_mut().message(level, message)
}
Expand Down
Loading

0 comments on commit c332a6f

Please sign in to comment.