summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2026-03-04 10:14:38 +0800
committer魏曹先生 <1992414357@qq.com>2026-03-04 15:25:43 +0800
commitf65b6a9eb6bd9d295b9adb5b95e0973f6e43954c (patch)
tree5f3b40271ee0dd259aff29488f65803028376939
parent05b7b483056902a07f83bc14f443ab59ce1471d8 (diff)
Bump version to 0.1.2
- Add increase/reduce functions (#1)
-rw-r--r--Cargo.lock2
-rw-r--r--Cargo.toml2
-rw-r--r--README.md10
-rw-r--r--src/lib.rs26
-rw-r--r--src/progress.rs48
-rw-r--r--src/renderer.rs72
6 files changed, 101 insertions, 59 deletions
diff --git a/Cargo.lock b/Cargo.lock
index b4be7da..d84bb5f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -32,7 +32,7 @@ dependencies = [
[[package]]
name = "just_progress"
-version = "0.1.1"
+version = "0.1.3"
dependencies = [
"tokio",
]
diff --git a/Cargo.toml b/Cargo.toml
index 38d9fa4..ea26462 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -2,7 +2,7 @@
name = "just_progress"
authors = ["Weicao-CatilGrass"]
description = "Just a progress display tool"
-version = "0.1.1"
+version = "0.1.2"
edition = "2024"
readme = "README.md"
diff --git a/README.md b/README.md
index 517ae53..70a0485 100644
--- a/README.md
+++ b/README.md
@@ -33,35 +33,35 @@ async fn proc() {
let download_task = async {
for i in 1..21 {
sleep(Duration::from_millis(50)).await;
- progress::update_progress("Download/Data", i as f32 * 0.05);
+ progress::update_progress(r"Download\/Data", i as f32 * 0.05);
}
};
let extract_task = async {
for i in 1..11 {
sleep(Duration::from_millis(100)).await;
- progress::update_progress("Download/Extract", i as f32 * 0.1);
+ progress::update_progress(r"Download\/Extract", i as f32 * 0.1);
}
};
let verify_sha256_task = async {
for i in 1..6 {
sleep(Duration::from_millis(300)).await;
- progress::update_progress("Download/Verify/SHA256", i as f32 * 0.2);
+ progress::update_progress(r"Download\/Verify\/SHA256", i as f32 * 0.2);
}
};
let verify_md5_task = async {
for i in 1..6 {
sleep(Duration::from_millis(100)).await;
- progress::update_progress("Download/Verify/MD5", i as f32 * 0.2);
+ progress::update_progress(r"Download\/Verify\/MD5", i as f32 * 0.2);
}
};
let verify_blake3_task = async {
for i in 1..6 {
sleep(Duration::from_millis(500)).await;
- progress::update_progress("Download/Verify/Blake3", i as f32 * 0.2);
+ progress::update_progress(r"Download\/Verify\/Blake3", i as f32 * 0.2);
}
};
diff --git a/src/lib.rs b/src/lib.rs
index 2875c41..ccf7495 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -47,21 +47,21 @@
//! let verify_sha256_task = async {
//! for i in 1..6 {
//! sleep(Duration::from_millis(300)).await;
-//! progress::update_progress("Download/Verify/SHA256", i as f32 * 0.2);
+//! progress::update_progress(r"Download\/Verify\/SHA256", i as f32 * 0.2);
//! }
//! };
//!
//! let verify_md5_task = async {
//! for i in 1..6 {
//! sleep(Duration::from_millis(100)).await;
-//! progress::update_progress("Download/Verify/MD5", i as f32 * 0.2);
+//! progress::update_progress(r"Download\/Verify\/MD5", i as f32 * 0.2);
//! }
//! };
//!
//! let verify_blake3_task = async {
//! for i in 1..6 {
//! sleep(Duration::from_millis(500)).await;
-//! progress::update_progress("Download/Verify/Blake3", i as f32 * 0.2);
+//! progress::update_progress(r"Download\/Verify\/Blake3", i as f32 * 0.2);
//! }
//! };
//!
@@ -110,17 +110,17 @@
/// ```rust
/// # use just_progress::progress::{self, ProgressInfo};
/// // Update progress value
-/// progress::update_progress("download/file1", 0.5);
+/// progress::update_progress(r"download\/file1", 0.5);
///
/// // Update status information
-/// progress::update_info("download/file1", ProgressInfo::Info("Downloading..."));
+/// progress::update_info(r"download\/file1", ProgressInfo::Info("Downloading..."));
///
/// // Update both progress and status simultaneously
-/// progress::update("download/file1", 0.75, ProgressInfo::Warning("Slow network"));
+/// progress::update(r"download\/file1", 0.75, ProgressInfo::Warning("Slow network"));
///
/// // Mark as complete
/// // Equivalent to progress::update_progress("download/file1", 1.0);
-/// progress::complete("download/file1");
+/// progress::complete(r"download\/file1");
///
/// // Clear all progress items
/// progress::clear_all();
@@ -142,12 +142,14 @@
///
/// # Hierarchical Progress
///
-/// Progress names can use slashes (`/`) to represent hierarchical relationships, e.g., `"parent/child"`.
+/// Progress names can use backslash-slash (`\/`) to represent hierarchical relationships, e.g., `r"parent\/child"`.
/// Renderers can leverage this structure to display indentation or calculate the average progress of parent tasks.
+/// The `\/` separator is used instead of `/` to allow file paths to be used as progress names.
///
/// # Notes
///
/// - `init()` must be called before using any other functions.
+/// - When using subprogress, use `\/` as the separator in progress names (e.g., `r"parent\/child"`).
/// - The Future returned by `bind()` needs to be polled or awaited for callbacks to execute.
/// - `clear_all()` sends a clear signal but does not immediately clear messages in the channel.
/// - `close()` stops all `bind()` calls; progress updates are no longer possible afterward.
@@ -187,10 +189,10 @@ pub mod progress;
///
/// # Subprogress
///
-/// When `subprogress` is enabled, progress names can be separated by slashes (`/`) to represent hierarchical relationships:
-/// - `"parent"` - Top-level progress
-/// - `"parent/child"` - Subprogress, automatically indented when displayed
-/// - `"parent/child/grandchild"` - Deeper level progress
+/// When `subprogress` is enabled, progress names can be separated by backslash-slash (`\/`) to represent hierarchical relationships:
+/// - `r"parent"` - Top-level progress
+/// - `r"parent\/child"` - Subprogress, automatically indented when displayed
+/// - `r"parent\/child\/grandchild"` - Deeper level progress
///
/// If a parent node does not have a directly corresponding progress state, the renderer will automatically calculate the average progress of its child nodes as the display value.
///
diff --git a/src/progress.rs b/src/progress.rs
index a5509ef..977ffc1 100644
--- a/src/progress.rs
+++ b/src/progress.rs
@@ -17,10 +17,8 @@ pub struct ProgressCenter {
/// ``` rust
/// use just_progress::progress;
///
-/// fn main() {
-/// // Initialize
-/// let progress_center = progress::init();
-/// }
+/// // Initialize
+/// let progress_center = progress::init();
/// ```
pub fn init() -> &'static ProgressCenter {
let (tx, rx) = watch::channel::<HashMap<String, ProgressState>>(HashMap::new());
@@ -55,11 +53,11 @@ where
let state = rx.borrow().clone();
// If it's a close flag, break
- if let Some(_) = state.get(SPECIAL_FLAG_CLOSE) {
+ if state.contains_key(SPECIAL_FLAG_CLOSE) {
break;
}
// Clear flag
- if let Some(_) = state.get(SPECIAL_FLAG_CLEAR) {
+ if state.contains_key(SPECIAL_FLAG_CLEAR) {
// Send all known names + progress 0 + EmptyState
for (name, _) in state.iter().filter(|(k, _)| *k != SPECIAL_FLAG_CLEAR) {
callback(
@@ -137,6 +135,42 @@ pub fn complete(name: &str) {
update_progress(name, 1.);
}
+/// Increase
+///
+/// Increase a progress item's value by a specified amount (clamped to 1.0)
+pub fn increase(name: &str, amount: f32) {
+ let Some(center) = PROGRESS_CENTER.get() else {
+ return;
+ };
+ let mut state = center.tx.borrow().clone();
+ let entry = state
+ .entry(name.to_string())
+ .or_insert_with(|| ProgressState {
+ progress: 0.0,
+ info: ProgressInfo::default(),
+ });
+ entry.progress = (entry.progress + amount).clamp(0.0, 1.0);
+ let _ = center.tx.send(state);
+}
+
+/// Reduce
+///
+/// Reduce a progress item's value by a specified amount (clamped to 0.0)
+pub fn reduce(name: &str, amount: f32) {
+ let Some(center) = PROGRESS_CENTER.get() else {
+ return;
+ };
+ let mut state = center.tx.borrow().clone();
+ let entry = state
+ .entry(name.to_string())
+ .or_insert_with(|| ProgressState {
+ progress: 0.0,
+ info: ProgressInfo::default(),
+ });
+ entry.progress = (entry.progress - amount).clamp(0.0, 1.0);
+ let _ = center.tx.send(state);
+}
+
/// Clear all
///
/// Send a message to clear all items, ensuring no progress remains
@@ -177,7 +211,7 @@ pub fn update_progress(name: &str, progress: f32) {
progress: 0.0,
info: ProgressInfo::default(),
});
- entry.progress = progress;
+ entry.progress = progress.clamp(0.0, 1.0);
let _ = center.tx.send(state);
}
diff --git a/src/renderer.rs b/src/renderer.rs
index ccd083b..53aedec 100644
--- a/src/renderer.rs
+++ b/src/renderer.rs
@@ -5,7 +5,7 @@ use std::io::{self, Write};
use std::sync::{Arc, Mutex};
pub struct ProgressSimpleRenderer<'a> {
- /// Use sub-progress (split by "/", indent by level)
+ /// Use sub-progress (split by "\/", indent by level)
/// If no relevant parent node exists, show parent progress as overall percentage of child progress
pub subprogress: bool,
@@ -25,6 +25,12 @@ pub struct ProgressSimpleRenderer<'a> {
last_line_count: Arc<Mutex<usize>>,
}
+impl<'a> Default for ProgressSimpleRenderer<'a> {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
impl<'a> ProgressSimpleRenderer<'a> {
/// Create a new renderer
pub fn new() -> Self {
@@ -151,11 +157,11 @@ impl<'a> ProgressSimpleRenderer<'a> {
let mut parent_items: Vec<String> = Vec::new();
for item in items.iter() {
- let parts: Vec<&str> = item.name.split('/').collect();
+ let parts: Vec<&str> = item.name.split(r"\/").collect();
if parts.len() > 1 {
// Build parent node path
for i in 1..parts.len() {
- let parent_path = parts[0..i].join("/");
+ let parent_path = parts[0..i].join(r"\/");
if !parent_items.contains(&parent_path) {
parent_items.push(parent_path);
}
@@ -165,7 +171,7 @@ impl<'a> ProgressSimpleRenderer<'a> {
// Compute depth for each item
for item in items.iter_mut() {
- item.depth = item.name.matches('/').count();
+ item.depth = item.name.matches(r"\/").count();
}
// Add missing parent nodes
@@ -174,7 +180,7 @@ impl<'a> ProgressSimpleRenderer<'a> {
// Compute parent node progress
let child_progress: Vec<f32> = items
.iter()
- .filter(|item| item.name.starts_with(&format!("{}/", parent_path)))
+ .filter(|item| item.name.starts_with(&format!("{}{}", parent_path, r"\/")))
.map(|item| item.progress)
.collect();
@@ -198,7 +204,8 @@ impl<'a> ProgressSimpleRenderer<'a> {
let item_names: Vec<String> = items.iter().map(|item| item.name.clone()).collect();
for item in items.iter_mut() {
if item_names.iter().any(|other_name| {
- other_name != &item.name && other_name.starts_with(&format!("{}/", item.name))
+ other_name != &item.name
+ && other_name.starts_with(&format!("{}{}", item.name, r"\/"))
}) {
item.is_parent = true;
}
@@ -206,7 +213,7 @@ impl<'a> ProgressSimpleRenderer<'a> {
}
/// Sort items according to sorting rule
- fn sort_items(&self, items: &mut Vec<ProgressItem>) {
+ fn sort_items(&self, items: &mut [ProgressItem]) {
if self.subprogress {
items.sort_by(|a, b| match self.sorting {
RendererSortingRule::Append => a.name.cmp(&b.name),
@@ -239,7 +246,7 @@ impl<'a> ProgressSimpleRenderer<'a> {
};
let display_name_len = if self.subprogress {
// For sub-progress, only show the last part
- item.name.split('/').last().unwrap_or(&item.name).len()
+ item.name.split(r"\/").last().unwrap_or(&item.name).len()
} else {
item.name.len()
};
@@ -358,7 +365,7 @@ impl<'a> ProgressSimpleRenderer<'a> {
// Only show the last part of the path
let display_name = if self.subprogress {
item.name
- .split('/')
+ .split(r"\/")
.last()
.unwrap_or(&item.name)
.to_string()
@@ -550,7 +557,7 @@ mod tests {
// Test update and rendering
renderer.update(
- "task1".to_string(),
+ r"parent\/task1".to_string(),
ProgressState {
progress: 0.5,
info: ProgressInfo::Info("Processing"),
@@ -558,10 +565,10 @@ mod tests {
);
renderer.update(
- "task2".to_string(),
+ r"parent\/task2".to_string(),
ProgressState {
progress: 0.75,
- info: ProgressInfo::Warning("Almost done"),
+ info: ProgressInfo::Info("Processing"),
},
);
@@ -574,7 +581,7 @@ mod tests {
let renderer = ProgressSimpleRenderer::new().with_subprogress(true);
renderer.update(
- "download/file1".to_string(),
+ r"download\/file1".to_string(),
ProgressState {
progress: 0.5,
info: ProgressInfo::Info("Downloading file1"),
@@ -582,7 +589,7 @@ mod tests {
);
renderer.update(
- "download/file2".to_string(),
+ r"download\/file2".to_string(),
ProgressState {
progress: 0.75,
info: ProgressInfo::Info("Downloading file2"),
@@ -654,9 +661,8 @@ mod tests {
.with_subprogress(true)
.with_theme(theme);
- // Add multi-level progress
renderer.update(
- "parent".to_string(),
+ r"parent".to_string(),
ProgressState {
progress: 0.5,
info: ProgressInfo::Info("Parent task"),
@@ -664,7 +670,7 @@ mod tests {
);
renderer.update(
- "parent/child".to_string(),
+ r"parent\/child".to_string(),
ProgressState {
progress: 0.75,
info: ProgressInfo::Info("Child task"),
@@ -672,7 +678,7 @@ mod tests {
);
renderer.update(
- "parent/child/grandchild".to_string(),
+ r"parent\/child\/grandchild".to_string(),
ProgressState {
progress: 0.9,
info: ProgressInfo::Info("Grandchild task"),
@@ -685,9 +691,9 @@ mod tests {
// Check internal state
{
let states = renderer.states.lock().unwrap();
- assert!(states.contains_key("parent"));
- assert!(states.contains_key("parent/child"));
- assert!(states.contains_key("parent/child/grandchild"));
+ assert!(states.contains_key(r"parent"));
+ assert!(states.contains_key(r"parent\/child"));
+ assert!(states.contains_key(r"parent\/child\/grandchild"));
}
}
@@ -703,18 +709,18 @@ mod tests {
// Add test data
renderer.update(
- "task1".to_string(),
+ r"parent\/child1".to_string(),
ProgressState {
progress: 0.5,
- info: ProgressInfo::Info("Processing"),
+ info: ProgressInfo::Info("Processing child1"),
},
);
renderer.update(
- "task2".to_string(),
+ r"parent\/child2".to_string(),
ProgressState {
progress: 0.75,
- info: ProgressInfo::Warning("Almost done"),
+ info: ProgressInfo::Info("Processing child2"),
},
);
@@ -724,8 +730,8 @@ mod tests {
// Check internal state
{
let states = renderer.states.lock().unwrap();
- assert!(states.contains_key("task1"));
- assert!(states.contains_key("task2"));
+ assert!(states.contains_key(r"parent\/child1"));
+ assert!(states.contains_key(r"parent\/child2"));
}
}
@@ -817,7 +823,7 @@ mod tests {
// Add parent and child nodes
renderer.update(
- "progress".to_string(),
+ r"progress".to_string(),
ProgressState {
progress: 0.95,
info: ProgressInfo::Info("Processing"),
@@ -825,7 +831,7 @@ mod tests {
);
renderer.update(
- "progress/2".to_string(),
+ r"progress\/2".to_string(),
ProgressState {
progress: 0.95,
info: ProgressInfo::Info("Step 2"),
@@ -833,7 +839,7 @@ mod tests {
);
renderer.update(
- "progress/3".to_string(),
+ r"progress\/3".to_string(),
ProgressState {
progress: 0.8,
info: ProgressInfo::Info("Step 3"),
@@ -846,9 +852,9 @@ mod tests {
// Check internal state
{
let states = renderer.states.lock().unwrap();
- assert!(states.contains_key("progress"));
- assert!(states.contains_key("progress/2"));
- assert!(states.contains_key("progress/3"));
+ assert!(states.contains_key(r"progress"));
+ assert!(states.contains_key(r"progress\/2"));
+ assert!(states.contains_key(r"progress\/3"));
}
}
}