diff --git a/Cargo.lock b/Cargo.lock index 8a977c4..3a1ce41 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,47 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "libc" +version = "0.2.174" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" + [[package]] name = "snowy_libs" -version = "0.1.2" +version = "0.1.3" +dependencies = [ + "termsize", +] + +[[package]] +name = "termsize" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f11ff5c25c172608d5b85e2fb43ee9a6d683a7f4ab7f96ae07b3d8b590368fd" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml index cd77a15..7a90527 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "snowy_libs" -version = "0.1.2" +version = "0.1.3" edition = "2024" [dependencies] +termsize = "0.1.9" diff --git a/src/lib.rs b/src/lib.rs index f8bb913..4fcf304 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,7 @@ +use std::io::Write; +use std::thread::JoinHandle; use std::{ + io, sync::{ Arc, atomic::{AtomicU64, Ordering}, @@ -7,77 +10,118 @@ use std::{ time::{Duration, Instant}, }; +/// This function creates a thread where it will print a progress bar based on the done count that you will update. +/// +/// Stops when done >= total +/// +/// Size is a relative value that is between 0-10 pub fn start_progress_bar( done: &Arc, total: u64, size: u8, title: &str, - started_at: Instant, update_time: Duration, -) { +) -> JoinHandle<()> { + assert!(size <= 10, "Size must be between 0 and 10"); let done = done.clone(); let title = title.to_owned(); - // let total = total.clone(); - // let size = size.clone(); + + // let mut cycles = vec![]; thread::spawn(move || { + let mut string = String::with_capacity(256); + let started_at = Instant::now(); + loop { + let mod_size = match termsize::get() { + Some(val) => val.cols - ((val.cols / 10) * (size as u16)), + None => size as u16, + }; + let done = done.load(Ordering::Relaxed); // some work here - let procentage_f = done as f32 / total as f32; - let procentage = (procentage_f * 100f32).round(); - // print!("{}") - let done_count = (size as f32 * procentage_f) as u64; + let percentage_f = done as f32 / total as f32; + let percentage = (percentage_f * 100f32).round() as u8; + let done_bars = (mod_size as f32 * percentage_f) as u16; - let mut string = "\r".to_string(); - string.push_str(&title); - string.push_str(" [[ "); + string.clear(); + string.push_str(&format!("\r\x1b[2K{} [[ ", title)); - if done == total { + // Make the cubes green when its finished + if done >= total { string.push_str("\x1b[32m"); } - for each in 0..size { - if each as u64 <= done_count { - string.push('■'); + // Print the progress bar + string.push_str(&"■".repeat(done_bars as usize)); + + // Make the dots darker + string.push_str("\x1b[2m"); + string.push_str(&"·".repeat((mod_size - done_bars) as usize)); + + // Reset any styling + string.push_str("\x1b[0m"); + + let going_for = started_at.elapsed().as_secs_f32(); + + if done == 0 { + string.push_str(&format!(" {}/{} {}% ]] ..", done, total, percentage)); + } else { + let time = { + if done >= total { + let time = going_for.round() as u64; + + string.push_str(&format!(" {}/{} 100% ]] in ", total, total,)); + time + } else { + // Guesstimate remaining time + let time_per_num = going_for / done as f32; + let remaining_time = (time_per_num * (total - done) as f32).ceil() as u64; + + string.push_str(&format!(" {}/{} {}% ]] ", done, total, percentage)); + remaining_time + } + }; + + let hours = time / 3600; + let minutes = (time % 3600) / 60; + let seconds = time % 60; + + if hours > 0 { + string.push_str(&format!("{hours}h {minutes}m {seconds}s")); + } else if minutes > 0 { + string.push_str(&format!("{minutes}m {seconds}s")); } else { - string.push('·'); + string.push_str(&format!("{seconds}s")); } } - if done == total { - string.push_str("\x1b[0m"); - } - - // Guestimate remaining time - let going_for = started_at.elapsed().as_secs_f32(); - let time_per_num = (done as f32 / going_for) / 100f32; - let remaining_time = (time_per_num * (total - done) as f32).ceil() as u64; - - if done == total { - string.push_str(&format!( - " {}/{} {}% ]] in {}s! \n", - done, - total, - procentage, - going_for.round() - )); + if done >= total { + string.push_str("!\n"); } else { - string.push_str(&format!( - " {}/{} {}% ]] {}s ", - done, total, procentage, remaining_time - )); + string.push(' '); } print!("{string}"); + let _ = io::stdout().flush(); // Try to flush + + // cycles.push(started_at.elapsed().as_micros()); if done >= total { break; } thread::sleep(update_time); } - }); + + // let mut avg = 0; + // for x in &cycles { + // avg += x; + // } + + // avg /= cycles.len() as u128; + // println!("Took on average {}μs ", avg); + }) } #[cfg(test)] @@ -91,24 +135,21 @@ mod tests { #[test] fn test_progress() { - let now = Instant::now(); - let value = Arc::new(AtomicU64::from(0)); - start_progress_bar( - &value, - 1811, - 50, - "Being silly", - now, - Duration::from_millis(30), - ); + let thread = start_progress_bar(&value, 511, 6, "Writing", Duration::from_millis(100)); + thread::spawn(move || { + thread::sleep(Duration::from_secs(1)); - for _ in 0..=12811 { - value.fetch_add(1, Ordering::Relaxed); + for _ in 0..511 { + value.fetch_add(1, Ordering::Relaxed); - thread::sleep(Duration::from_millis(100)); - } - // assert_eq!(result, 4); + thread::sleep(Duration::from_millis(100)); + } + }); + + thread.join().unwrap(); + + println!("finished!"); } }