diff options
| author | 魏曹先生 <1992414357@qq.com> | 2026-03-04 10:14:38 +0800 |
|---|---|---|
| committer | 魏曹先生 <1992414357@qq.com> | 2026-03-04 15:25:43 +0800 |
| commit | f65b6a9eb6bd9d295b9adb5b95e0973f6e43954c (patch) | |
| tree | 5f3b40271ee0dd259aff29488f65803028376939 | |
| parent | 05b7b483056902a07f83bc14f443ab59ce1471d8 (diff) | |
Bump version to 0.1.2
- Add increase/reduce functions (#1)
| -rw-r--r-- | Cargo.lock | 2 | ||||
| -rw-r--r-- | Cargo.toml | 2 | ||||
| -rw-r--r-- | README.md | 10 | ||||
| -rw-r--r-- | src/lib.rs | 26 | ||||
| -rw-r--r-- | src/progress.rs | 48 | ||||
| -rw-r--r-- | src/renderer.rs | 72 |
6 files changed, 101 insertions, 59 deletions
@@ -32,7 +32,7 @@ dependencies = [ [[package]] name = "just_progress" -version = "0.1.1" +version = "0.1.3" dependencies = [ "tokio", ] @@ -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" @@ -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); } }; @@ -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")); } } } |
