From c33ef71e554dff4e3b664492d6145dc395cf5da6 Mon Sep 17 00:00:00 2001 From: Nikkuss Date: Wed, 12 Nov 2025 21:54:45 +0400 Subject: [PATCH] work on download progress bars --- src/download_pb.rs | 90 ++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 + src/main.rs | 34 +++++++++-------- src/state_manager.rs | 54 +++++++++----------------- 4 files changed, 129 insertions(+), 51 deletions(-) create mode 100644 src/download_pb.rs diff --git a/src/download_pb.rs b/src/download_pb.rs new file mode 100644 index 0000000..d1518cc --- /dev/null +++ b/src/download_pb.rs @@ -0,0 +1,90 @@ +use std::{cell::RefCell, rc::Rc}; + +use indicatif::{MultiProgress, ProgressBar}; + +use crate::multibar::{BarSegment, MultiBar}; + +#[derive(Debug, Clone)] +pub struct DownloadBar { + pub bar: ProgressBar, + pub name: String, + pub download_expected: Rc>, + pub download_done: Rc>, + pub extract_expected: Rc>, + pub extract_done: Rc>, +} + +impl DownloadBar { + pub fn new(bar: ProgressBar, name: String, width: u16) -> Self { + bar.set_style(indicatif::ProgressStyle::with_template("{msg}").unwrap()); + let new_self = Self { + name, + bar, + download_expected: Rc::new(RefCell::new(0)), + download_done: Rc::new(RefCell::new(0)), + extract_expected: Rc::new(RefCell::new(0)), + extract_done: Rc::new(RefCell::new(0)), + }; + new_self.update(width); + new_self + } + + pub fn update(&self, width: u16) { + let download_done = (*self.download_done.borrow()); + let download_expected = (*self.download_expected.borrow()); + let extract_done = *self.extract_done.borrow(); + let extract_expected = *self.extract_expected.borrow(); + + let name = pad_string(&self.name, 20); + if download_expected == 0 || extract_expected == 0 { + self.bar.set_message(format!("Download {}", name)); + return; + } + + let total_expected = download_expected + extract_expected; + let total_done = download_done + extract_done; + + let dl_percent = ((download_done as f64 / download_expected as f64) * 100.0) as u64; + let ex_percent = ((extract_done as f64 / extract_expected as f64) * 100.0) as u64; + + let msg = match width { + 0..50 => { + format!( + "{}: {}/{}", + self.name, + total_done, + if total_expected == 0 { + "-".to_string() + } else { + total_expected.to_string() + } + ) + } + _ => { + let bar_dl = MultiBar([ + BarSegment::Dynamic("=", download_done), + BarSegment::Dynamic(" ", download_expected.saturating_sub(download_done)), + ]) + .scale((width / 6) as u64); + let bar_ex = MultiBar([ + BarSegment::Dynamic("=", extract_done), + BarSegment::Dynamic(" ", extract_expected.saturating_sub(extract_done)), + ]) + .scale((width / 6) as u64); + format!("Download {name} [{bar_dl}] {dl_percent:3}% [{bar_ex}] {ex_percent:3}%",) + } + }; + self.bar.set_message(msg); + self.bar.tick(); + } +} + +fn pad_string(s: &str, width: usize) -> String { + if s.len() >= width { + s.to_string() + } else { + let mut padded = s.to_string(); + padded.push_str(&" ".repeat(width - s.len())); + padded + } +} diff --git a/src/lib.rs b/src/lib.rs index 157775c..b4b9130 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,6 @@ pub mod action; pub mod action_raw; +pub mod download_pb; +pub mod multibar; pub mod nix_path; pub mod state_manager; diff --git a/src/main.rs b/src/main.rs index 5e17a1d..5834187 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,6 +17,7 @@ static LAZY: LazyLock = LazyLock::new(|| { pub mod action; pub mod action_raw; +pub mod download_pb; pub mod multibar; pub mod nix_path; pub mod state_manager; @@ -71,7 +72,7 @@ impl TermLike for TextTerm { fn main() -> Result<(), color_eyre::Report> { color_eyre::install().unwrap(); - println!("Lazy value: {}", *LAZY); + // println!("Lazy value: {}", *LAZY); // let pb = ProgressBar::new(100); // pb.set_draw_target(indicatif::ProgressDrawTarget::term_like(Box::new(TextTerm))); // @@ -86,25 +87,27 @@ fn main() -> Result<(), color_eyre::Report> { // sleep(Duration::from_millis(50)); // } // pb.finish_and_clear(); - let term = Term::stdout(); - let empty = style("-").blue().to_string(); - let bar = MultiBar([ - BarSegment::Dynamic("=", 10), - BarSegment::Static(">"), - BarSegment::Dynamic(empty.as_str(), 3), - BarSegment::Dynamic(" ", 50), - ]) - .scale(u64::from(term.size().1 - 2)); - println!("[{}]", bar); - return Ok(()); + // let empty = style("-").blue().to_string(); + // let bar = MultiBar([ + // BarSegment::Dynamic("=", 10), + // BarSegment::Static(">"), + // BarSegment::Dynamic(empty.as_str(), 3), + // BarSegment::Dynamic(" ", 50), + // ]) + // .scale(u64::from(term.size().1 - 2)); + // println!("[{}]", bar); + // return Ok(()); let lines = std::fs::read_to_string("build.log")?; // let lines = lines.lines().take(0).collect::>().join("\n"); - + let term = Term::stdout(); let mut state = State { multi_progress: MultiProgress::new(), manager: StateManager::new(), separator: None, + width: term.size().1, + current_width: term.size().1, + term, }; // let progress = MultiProgress::new(); @@ -224,8 +227,9 @@ fn main() -> Result<(), color_eyre::Report> { if expected == 0 { continue; }; - if let Some(child) = state.manager.get_mut(*id) { - child.progress(done, expected); + if let Some(mut child) = state.manager.take(*id) { + child.progress(&mut state, done, expected); + state.manager.insert_parent(*id, child); } // sleep(Duration::from_millis(1)); // if let Some(build_state) = state.manager.get_mut(*id) diff --git a/src/state_manager.rs b/src/state_manager.rs index a9cce68..b514ab8 100644 --- a/src/state_manager.rs +++ b/src/state_manager.rs @@ -1,13 +1,14 @@ -use console::style; +use console::{Term, style}; use indicatif::{MultiProgress, ProgressBar, ProgressFinish, ProgressStyle}; use std::{borrow::Cow, collections::HashMap, io}; -use crate::{action::StartFields, nix_path}; +use crate::{action::StartFields, download_pb::DownloadBar, nix_path}; pub struct State<'a> { pub multi_progress: MultiProgress, pub manager: StateManager<'a>, pub separator: Option, + pub term: Term, pub width: u16, pub current_width: u16, } @@ -56,12 +57,11 @@ pub enum BuildEnumState<'a> { }, SubstituteCopy { path: Cow<'a, str>, - bar: ProgressBar, + bar: DownloadBar, }, SubstituteFetch { path: Cow<'a, str>, - extract_bar: ProgressBar, - download_bar: ProgressBar, + bar: DownloadBar, }, Query { path: Cow<'a, str>, @@ -105,32 +105,13 @@ impl<'a> BuildState<'a> { (BuildEnumState::Substitute { .. }, StartFields::CopyPath { path, .. }) => { let name = nix_path::extract_package_name(&path).unwrap_or("unknown".to_string()); let bar = state.add_pb(ProgressBar::new(100)); - bar.set_message(format!("Copying substitute for {}", name)); - bar.set_style( - ProgressStyle::default_bar() - .template("{prefix}{msg} [{bar:40.cyan/blue}] {pos:>3}%") - .unwrap() - .progress_chars("=> "), - ); - bar.tick(); + let bar = DownloadBar::new(bar, name, state.width); BuildEnumState::SubstituteCopy { path, bar } } (BuildEnumState::SubstituteCopy { path, bar }, StartFields::FileTransfer { .. }) => { - let download_bar = state.add_pb_after(bar, ProgressBar::new(100)); - let name = nix_path::extract_package_name(path).unwrap_or("unknown".to_string()); - download_bar.set_message(format!("Fetching substitute for {}", name)); - download_bar.set_style( - ProgressStyle::default_bar() - .template("{prefix}{msg} [{bar:40.cyan/blue}] {pos:>3}%") - .unwrap() - .progress_chars("=> "), - ); - download_bar.set_prefix("=== "); - download_bar.tick(); BuildEnumState::SubstituteFetch { path: path.clone(), - extract_bar: bar.clone(), - download_bar, + bar: bar.clone(), } } (_, StartFields::Unknown) => BuildEnumState::Unknown, @@ -153,19 +134,17 @@ impl<'a> BuildState<'a> { _ => {} } } - pub fn progress(&mut self, done: u64, expected: u64) { + pub fn progress(&mut self, state: &mut State<'a>, done: u64, expected: u64) { match &self.state { - BuildEnumState::SubstituteFetch { download_bar, .. } => { - let percentage = (done * 100 / expected) as u64; - if percentage > download_bar.position() { - download_bar.set_position(percentage); - }; + BuildEnumState::SubstituteFetch { bar, .. } => { + bar.download_expected.replace(expected); + bar.download_done.replace(done); + bar.update(state.width); } BuildEnumState::SubstituteCopy { path, bar } => { - let percentage = (done * 100 / expected) as u64; - if percentage > bar.position() { - bar.set_position(percentage); - }; + bar.extract_expected.replace(expected); + bar.extract_done.replace(done); + bar.update(state.width); } _ => {} } @@ -194,6 +173,9 @@ impl<'a> StateManager<'a> { pub fn get_or_insert(&mut self, id: u64) -> &mut BuildState<'a> { self.states.entry(id).or_insert_with(BuildState::new) } + pub fn take(&mut self, id: u64) -> Option> { + self.states.remove(&id) + } pub fn remove(&mut self, id: u64) { if let Some(state) = self.states.get(&id) { if let Some(pb) = &state.progress_bar {