work on multibar

This commit is contained in:
2025-11-11 13:29:01 +04:00
parent 15e8bf5e69
commit 50176e5db4
3 changed files with 131 additions and 47 deletions

View File

@@ -8,6 +8,7 @@ use crate::state_manager::{BuildEnumState, BuildState, State, StateManager};
pub mod action; pub mod action;
pub mod action_raw; pub mod action_raw;
pub mod multibar;
pub mod nix_path; pub mod nix_path;
pub mod state_manager; pub mod state_manager;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -62,35 +63,36 @@ impl TermLike for TextTerm {
fn main() -> Result<(), color_eyre::Report> { fn main() -> Result<(), color_eyre::Report> {
color_eyre::install().unwrap(); color_eyre::install().unwrap();
let pb = ProgressBar::new(100); // let pb = ProgressBar::new(100);
pb.set_draw_target(indicatif::ProgressDrawTarget::term_like(Box::new(TextTerm))); // 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( // return Ok(());
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(());
let lines = std::fs::read_to_string("build.log")?; let lines = std::fs::read_to_string("build.log")?;
let lines = lines.lines().take(0).collect::<Vec<_>>().join("\n"); // let lines = lines.lines().take(0).collect::<Vec<_>>().join("\n");
let mut state = State { let mut state = State {
progress: MultiProgress::new(), multi_progress: MultiProgress::new(),
manager: StateManager::new(), manager: StateManager::new(),
separator: None,
}; };
// let progress = MultiProgress::new(); // let progress = MultiProgress::new();
let lines_pb = state // let lines_pb = state
.progress // .multi_progress
.add(ProgressBar::new(lines.lines().count() as u64)); // .add(ProgressBar::new(lines.lines().count() as u64));
// progress.println("Parsing build.log...")?; // progress.println("Parsing build.log...")?;
// let pb1 = progress.add(ProgressBar::new(lines.lines().count() as u64)); // let pb1 = progress.add(ProgressBar::new(lines.lines().count() as u64));
// pb1.set_style( // pb1.set_style(
@@ -111,15 +113,13 @@ fn main() -> Result<(), color_eyre::Report> {
// let mut map = HashMap::new(); // let mut map = HashMap::new();
for line in lines.lines() { for line in lines.lines() {
lines_pb.inc(1); // lines_pb.inc(1);
let line = line.strip_prefix("@nix ").unwrap_or(line); let line = line.strip_prefix("@nix ").unwrap_or(line);
sleep(Duration::from_millis(1)); sleep(Duration::from_millis(1));
let action = action::Action::parse(line)?; let action = action::Action::parse(line)?;
match action { match action {
action::Action::Msg { level, msg } => { action::Action::Msg { level, msg } => {
state state.println(format!("MSG (level {level}): {msg}"))?;
.progress
.println(format!("MSG (level {level}): {msg}"))?;
} }
action::Action::Start { action::Action::Start {
start_type, start_type,
@@ -192,7 +192,7 @@ fn main() -> Result<(), color_eyre::Report> {
} }
action::Action::Result { id, fields } => match fields { action::Action::Result { id, fields } => match fields {
action::ResultFields::FetchStatus(status) => { action::ResultFields::FetchStatus(status) => {
state.progress.println(format!( state.println(format!(
"RESULT FetchStatus (id: {}): status={}", "RESULT FetchStatus (id: {}): status={}",
id, status id, status
))?; ))?;

39
src/multibar.rs Normal file
View File

@@ -0,0 +1,39 @@
use std::fmt;
#[derive(Debug)]
pub struct MultiBar<'s, const N: usize>(pub [(&'s str, u64); N]);
impl<const N: usize> 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<const N: usize> 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(())
}
}

View File

@@ -1,16 +1,43 @@
use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; use console::style;
use std::{borrow::Cow, collections::HashMap}; use indicatif::{MultiProgress, ProgressBar, ProgressFinish, ProgressStyle};
use std::{borrow::Cow, collections::HashMap, io};
use crate::{action::StartFields, nix_path}; use crate::{action::StartFields, nix_path};
pub struct State<'a> { pub struct State<'a> {
pub progress: MultiProgress, pub multi_progress: MultiProgress,
pub manager: StateManager<'a>, pub manager: StateManager<'a>,
pub separator: Option<ProgressBar>,
} }
impl<'a> State<'a> { impl<'a> State<'a> {
pub fn add_pb(&mut self, pb: ProgressBar) -> ProgressBar { 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<I: AsRef<str>>(&self, msg: I) -> io::Result<()> {
self.multi_progress.println(msg)
} }
} }
@@ -27,10 +54,12 @@ pub enum BuildEnumState<'a> {
}, },
SubstituteCopy { SubstituteCopy {
path: Cow<'a, str>, path: Cow<'a, str>,
bar: ProgressBar,
}, },
SubstituteFetch { SubstituteFetch {
path: Cow<'a, str>, path: Cow<'a, str>,
bar: ProgressBar, extract_bar: ProgressBar,
download_bar: ProgressBar,
}, },
Query { Query {
path: Cow<'a, str>, path: Cow<'a, str>,
@@ -74,12 +103,34 @@ impl<'a> BuildState<'a> {
BuildEnumState::Substitute { source, target } BuildEnumState::Substitute { source, target }
} }
(BuildEnumState::Substitute { .. }, StartFields::CopyPath { path, .. }) => { (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 { BuildEnumState::SubstituteFetch {
path: path.clone(), path: path.clone(),
bar: state.add_pb(ProgressBar::new(100)), extract_bar: bar.clone(),
download_bar,
} }
} }
(_, StartFields::Unknown) => BuildEnumState::Unknown, (_, StartFields::Unknown) => BuildEnumState::Unknown,
@@ -94,29 +145,23 @@ impl<'a> BuildState<'a> {
pub fn tick(&mut self, state: &mut State<'a>) { pub fn tick(&mut self, state: &mut State<'a>) {
match &self.state { match &self.state {
BuildEnumState::QueryFetch { target } => { 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.set_message(format!("Fetching info for {}", target));
pb.enable_steady_tick(std::time::Duration::from_millis(100)); pb.enable_steady_tick(std::time::Duration::from_millis(100));
self.progress_bar = Some(pb); 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) { pub fn progress(&mut self, done: u64, expected: u64) {
match &self.state { 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; let percentage = (done * 100 / expected) as u64;
if percentage > bar.position() { if percentage > bar.position() {
bar.set_position(percentage); bar.set_position(percentage);