From 50176e5db47595ee97e9ef12a46670fb520425fa Mon Sep 17 00:00:00 2001 From: Nikkuss Date: Tue, 11 Nov 2025 13:29:01 +0400 Subject: [PATCH] work on multibar --- src/main.rs | 50 ++++++++++++------------- src/multibar.rs | 39 +++++++++++++++++++ src/state_manager.rs | 89 +++++++++++++++++++++++++++++++++----------- 3 files changed, 131 insertions(+), 47 deletions(-) create mode 100644 src/multibar.rs diff --git a/src/main.rs b/src/main.rs index 645d89d..2e814ed 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,6 +8,7 @@ use crate::state_manager::{BuildEnumState, BuildState, State, StateManager}; pub mod action; pub mod action_raw; +pub mod multibar; pub mod nix_path; pub mod state_manager; #[derive(Debug, Clone)] @@ -62,35 +63,36 @@ impl TermLike for TextTerm { fn main() -> Result<(), color_eyre::Report> { color_eyre::install().unwrap(); - let pb = ProgressBar::new(100); - pb.set_draw_target(indicatif::ProgressDrawTarget::term_like(Box::new(TextTerm))); + // let pb = ProgressBar::new(100); + // pb.set_draw_target(indicatif::ProgressDrawTarget::term_like(Box::new(TextTerm))); + // + // pb.set_style( + // ProgressStyle::default_bar() + // .template("{msg} [{bar:40.cyan/blue}] {pos:>3}%") + // .unwrap(), + // ); + // pb.set_message("Example Progress Bar"); + // for i in 0..=5 { + // pb.set_position(i); + // sleep(Duration::from_millis(50)); + // } + // pb.finish_and_clear(); - pb.set_style( - ProgressStyle::default_bar() - .template("{msg} [{bar:40.cyan/blue}] {pos:>3}%") - .unwrap(), - ); - pb.set_message("Example Progress Bar"); - for i in 0..=5 { - pb.set_position(i); - sleep(Duration::from_millis(50)); - } - pb.finish_and_clear(); - - return Ok(()); + // return Ok(()); let lines = std::fs::read_to_string("build.log")?; - let lines = lines.lines().take(0).collect::>().join("\n"); + // let lines = lines.lines().take(0).collect::>().join("\n"); let mut state = State { - progress: MultiProgress::new(), + multi_progress: MultiProgress::new(), manager: StateManager::new(), + separator: None, }; // let progress = MultiProgress::new(); - let lines_pb = state - .progress - .add(ProgressBar::new(lines.lines().count() as u64)); + // let lines_pb = state + // .multi_progress + // .add(ProgressBar::new(lines.lines().count() as u64)); // progress.println("Parsing build.log...")?; // let pb1 = progress.add(ProgressBar::new(lines.lines().count() as u64)); // pb1.set_style( @@ -111,15 +113,13 @@ fn main() -> Result<(), color_eyre::Report> { // let mut map = HashMap::new(); for line in lines.lines() { - lines_pb.inc(1); + // lines_pb.inc(1); let line = line.strip_prefix("@nix ").unwrap_or(line); sleep(Duration::from_millis(1)); let action = action::Action::parse(line)?; match action { action::Action::Msg { level, msg } => { - state - .progress - .println(format!("MSG (level {level}): {msg}"))?; + state.println(format!("MSG (level {level}): {msg}"))?; } action::Action::Start { start_type, @@ -192,7 +192,7 @@ fn main() -> Result<(), color_eyre::Report> { } action::Action::Result { id, fields } => match fields { action::ResultFields::FetchStatus(status) => { - state.progress.println(format!( + state.println(format!( "RESULT FetchStatus (id: {}): status={}", id, status ))?; diff --git a/src/multibar.rs b/src/multibar.rs new file mode 100644 index 0000000..711ebf3 --- /dev/null +++ b/src/multibar.rs @@ -0,0 +1,39 @@ +use std::fmt; + +#[derive(Debug)] +pub struct MultiBar<'s, const N: usize>(pub [(&'s str, u64); N]); + +impl MultiBar<'_, N> { + /// Total length of the bar + pub(crate) fn length(&self) -> u64 { + self.0.iter().map(|(_, len)| *len).sum() + } + + /// Transforms the bar to be of target size + pub(crate) fn scale(&self, size: u64) -> Self { + let length = std::cmp::max(self.length(), 1); + let mut prev_prop = 0; + let mut curr_prop = 0; + + let inner = self.0.map(|(c, len)| { + curr_prop += len; + let nb_chars = size * curr_prop / length - size * prev_prop / length; + prev_prop = curr_prop; + (c, nb_chars) + }); + + Self(inner) + } +} + +impl fmt::Display for MultiBar<'_, N> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + for &(c, len) in &self.0 { + for _ in 0..len { + f.write_str(c)?; + } + } + + Ok(()) + } +} diff --git a/src/state_manager.rs b/src/state_manager.rs index 179f9e4..2e0c822 100644 --- a/src/state_manager.rs +++ b/src/state_manager.rs @@ -1,16 +1,43 @@ -use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; -use std::{borrow::Cow, collections::HashMap}; +use console::style; +use indicatif::{MultiProgress, ProgressBar, ProgressFinish, ProgressStyle}; +use std::{borrow::Cow, collections::HashMap, io}; use crate::{action::StartFields, nix_path}; pub struct State<'a> { - pub progress: MultiProgress, + pub multi_progress: MultiProgress, pub manager: StateManager<'a>, + pub separator: Option, } impl<'a> State<'a> { pub fn add_pb(&mut self, pb: ProgressBar) -> ProgressBar { - self.progress.add(pb) + let separator = self.separator.get_or_insert_with(|| { + let separator = ProgressBar::new_spinner() + .with_style( + ProgressStyle::default_spinner() + .template(&style("··{wide_msg:<}").dim().to_string()) + .expect("invalid template"), + ) + .with_message("·".repeat(512)) + .with_finish(ProgressFinish::AndClear); + + let separator = self.multi_progress.insert(0, separator); + separator.set_length(0); + separator + }); + + self.multi_progress.insert_after(separator, pb) + // self.progress.add(pb) + } + pub fn add_pb_before(&mut self, before: &ProgressBar, pb: ProgressBar) -> ProgressBar { + self.multi_progress.insert_before(before, pb) + } + pub fn add_pb_after(&mut self, after: &ProgressBar, pb: ProgressBar) -> ProgressBar { + self.multi_progress.insert_after(after, pb) + } + pub fn println>(&self, msg: I) -> io::Result<()> { + self.multi_progress.println(msg) } } @@ -27,10 +54,12 @@ pub enum BuildEnumState<'a> { }, SubstituteCopy { path: Cow<'a, str>, + bar: ProgressBar, }, SubstituteFetch { path: Cow<'a, str>, - bar: ProgressBar, + extract_bar: ProgressBar, + download_bar: ProgressBar, }, Query { path: Cow<'a, str>, @@ -74,12 +103,34 @@ impl<'a> BuildState<'a> { BuildEnumState::Substitute { source, target } } (BuildEnumState::Substitute { .. }, StartFields::CopyPath { path, .. }) => { - BuildEnumState::SubstituteCopy { 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(); + BuildEnumState::SubstituteCopy { path, bar } } - (BuildEnumState::SubstituteCopy { path }, StartFields::FileTransfer { .. }) => { + (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(), - bar: state.add_pb(ProgressBar::new(100)), + extract_bar: bar.clone(), + download_bar, } } (_, StartFields::Unknown) => BuildEnumState::Unknown, @@ -94,29 +145,23 @@ impl<'a> BuildState<'a> { pub fn tick(&mut self, state: &mut State<'a>) { match &self.state { BuildEnumState::QueryFetch { target } => { - let pb = state.progress.add(ProgressBar::new_spinner()); + let pb = state.add_pb(ProgressBar::new_spinner()); pb.set_message(format!("Fetching info for {}", target)); pb.enable_steady_tick(std::time::Duration::from_millis(100)); self.progress_bar = Some(pb); } - BuildEnumState::SubstituteFetch { path, bar } => { - let name = nix_path::extract_package_name(path).unwrap_or("unknown".to_string()); - bar.set_message(format!("Fetching substitute for {}", name)); - bar.set_style( - ProgressStyle::default_bar() - .template("{msg} [{bar:40.cyan/blue}] {pos:>3}%") - .unwrap() - .progress_chars("=> "), - ); - // bar.enable_steady_tick(std::time::Duration::from_millis(100)); - // self.progress_bar = Some(bar.clone()); - } _ => {} } } pub fn progress(&mut self, done: u64, expected: u64) { match &self.state { - BuildEnumState::SubstituteFetch { bar, .. } => { + BuildEnumState::SubstituteFetch { download_bar, .. } => { + let percentage = (done * 100 / expected) as u64; + if percentage > download_bar.position() { + download_bar.set_position(percentage); + }; + } + BuildEnumState::SubstituteCopy { path, bar } => { let percentage = (done * 100 / expected) as u64; if percentage > bar.position() { bar.set_position(percentage);