rewrite most of the handler code :3

This commit is contained in:
2025-11-13 20:02:22 +04:00
parent 52d6137c05
commit 0f725073d8
13 changed files with 1585 additions and 281 deletions

View File

@@ -6,6 +6,7 @@ use indicatif::{HumanBytes, MultiProgress, ProgressBar};
use crate::{
estimator::Estimator,
multibar::{BarSegment, MultiBar},
pad_string,
};
static DOWNLOAD_CHAR: LazyLock<String> = LazyLock::new(|| style("#").blue().bold().to_string());
@@ -136,13 +137,3 @@ fn str_len(s: &str) -> usize {
.filter(|x| x.is_alphanumeric() || x.is_whitespace())
.count()
}
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
}
}

238
src/handlers/download.rs Normal file
View File

@@ -0,0 +1,238 @@
use std::{sync::LazyLock, time::Instant};
use indicatif::{HumanBytes, ProgressBar};
use owo_colors::OwoColorize;
use crate::{
action::{Action, BuildStepId, ResultFields, StartFields},
estimator::Estimator,
handlers::{Handler, fetch::FetchHandler},
multibar::{BarSegment, MultiBar},
nix_path, pad_string,
};
static DOWNLOAD_CHAR: LazyLock<String> = LazyLock::new(|| "#".blue().bold().to_string());
static DONE_CHAR: LazyLock<String> = LazyLock::new(|| "#".green().bold().to_string());
const INFO_WIDTH: usize = 13;
pub struct SubstituteHandler;
impl Handler for SubstituteHandler {
fn handle(
&mut self,
state: &mut crate::state::State,
action: &crate::action::Action,
) -> color_eyre::Result<bool> {
match action {
Action::Start {
start_type: StartFields::Substitute { source, target },
id,
..
} => {
state.println(format!("Substituting {} -> {}", source, target))?;
state.plug(CopyHandler(*id));
// Handle global start action
Ok(true)
}
_ => Ok(true),
}
}
}
pub struct CopyHandler(BuildStepId);
impl Handler for CopyHandler {
fn handle(
&mut self,
state: &mut crate::state::State,
action: &crate::action::Action,
) -> color_eyre::Result<bool> {
match action {
Action::Start {
start_type:
StartFields::CopyPath {
path,
origin,
destination,
},
id,
parent,
..
} if parent == &self.0 => {
state.println(format!(
"Copying {} <- {} (from {})",
destination, path, origin
))?;
let bar = state.add_pb(ProgressBar::new(1));
state.plug(DownloadHandler::new(
state.term_width,
*id,
bar,
path.to_string(),
));
// Handle global start action
Ok(false)
}
_ => Ok(true),
}
}
}
pub struct DownloadHandler {
pub id: BuildStepId,
pub copy_id: BuildStepId,
pub download_estimator: Estimator,
pub bar: ProgressBar,
pub path: String,
pub download_expected: u64,
pub download_done: u64,
pub extract_expected: u64,
pub extract_done: u64,
pub started_at: Instant,
}
impl DownloadHandler {
pub fn new(width: u16, copy_id: BuildStepId, bar: ProgressBar, path: String) -> Self {
bar.set_style(indicatif::ProgressStyle::with_template("{msg}").unwrap());
let new = Self {
id: u64::MAX.into(),
copy_id,
download_estimator: Estimator::new(Instant::now()),
bar,
path,
download_expected: 0,
download_done: 0,
extract_expected: 0,
extract_done: 0,
started_at: Instant::now(),
};
new.draw_bar(width);
new
}
pub fn draw_bar(&self, width: u16) {
let name = nix_path::extract_package_name_string(&self.path)
.unwrap_or_else(|| "unknown".to_string());
let name = pad_string(&name, (width / 4) as usize);
let status = format!("Download {} |", name.purple().bold());
let status_len = (width / 4) as usize + 10;
if self.download_expected == 0 || self.extract_expected == 0 {
self.bar.set_message(format!("{status}"));
self.bar.tick();
return;
}
let total_expected = self.download_expected + self.extract_expected;
let total_done = self.download_done + self.extract_done;
let dl_percent =
((self.download_done as f64 / self.download_expected as f64) * 100.0) as u64;
let ex_percent = ((self.extract_done as f64 / self.extract_expected as f64) * 100.0) as u64;
let download_per_sec =
HumanBytes(self.download_estimator.steps_per_second(Instant::now()) as u64).to_string();
let download_done_human = HumanBytes(self.download_done).to_string();
let download_done_human = pad_string(&download_done_human, INFO_WIDTH)
.blue()
.bold()
.to_string();
let extract_done_human = HumanBytes(self.extract_done).to_string();
let extract_done_human = pad_string(&extract_done_human, INFO_WIDTH)
.green()
.bold()
.to_string();
let download_per_sec = pad_string(&format!("{download_per_sec}/s"), INFO_WIDTH)
.blue()
.bold()
.to_string();
let display = format!("{download_per_sec} | {download_done_human} | {extract_done_human} ");
let display_length = (INFO_WIDTH * 3) + 9;
// + 6 to account for final format
let total_length = status_len + display_length + 4;
let min = dl_percent.min(ex_percent);
let dl = dl_percent.saturating_sub(min);
let bar = MultiBar([
BarSegment::Dynamic(&DONE_CHAR, min),
BarSegment::Dynamic(&DOWNLOAD_CHAR, dl),
BarSegment::Dynamic(" ", 100 - min - dl),
])
.scale((width - total_length as u16) as u64);
let msg = match width {
0..50 => {
format!(
"{}: {}/{}",
name,
total_done,
if total_expected == 0 {
"-".to_string()
} else {
total_expected.to_string()
}
)
}
_ => {
format!("{status} {display} [{bar}]",)
}
};
self.bar.set_message(msg);
self.bar.tick();
}
}
impl Handler for DownloadHandler {
fn handle(
&mut self,
state: &mut crate::state::State,
action: &crate::action::Action,
) -> color_eyre::Result<bool> {
match action {
Action::Start {
start_type: StartFields::FileTransfer { target },
id,
parent,
..
} if parent == &self.copy_id => {
state.println(format!("Downloading {}", target))?;
self.id = *id;
// Handle global start action
Ok(true)
}
Action::Result {
fields:
ResultFields::Progress {
done,
expected,
running: _,
failed: _,
},
id,
} => {
if id == &self.id {
self.download_done = *done;
self.download_expected = *expected;
self.download_estimator.record(*done, Instant::now());
self.draw_bar(state.term_width);
}
if id == &self.copy_id {
self.extract_done = *done;
self.extract_expected = *expected;
self.draw_bar(state.term_width);
}
Ok(true)
}
Action::Stop { id } if id == &self.copy_id => {
self.bar.finish_and_clear();
Ok(false)
}
_ => Ok(true),
}
}
fn on_resize(&mut self, state: &mut crate::state::State) -> color_eyre::Result<()> {
self.draw_bar(state.term_width);
Ok(())
}
}

63
src/handlers/fetch.rs Normal file
View File

@@ -0,0 +1,63 @@
use std::borrow::Cow;
use indicatif::ProgressBar;
use owo_colors::OwoColorize;
use crate::{action::Action, handlers::Handler, nix_path, pad_string};
pub struct FetchHandler {
id: u64,
bar: ProgressBar,
path: String,
source: String,
}
impl FetchHandler {
pub fn new<'a>(
id: u64,
width: u16,
path: Cow<'a, str>,
source: Cow<'a, str>,
bar: ProgressBar,
) -> Self {
bar.enable_steady_tick(std::time::Duration::from_millis(100));
let new = Self {
id,
bar,
path: path.to_string(),
source: source.to_string(),
};
new.format(width);
new
}
fn format(&self, width: u16) {
let name = nix_path::extract_package_name_string(&self.path)
.expect("Failed to extract package name");
let chunk = (width - 15) / 2;
let name = pad_string(&name, chunk as usize);
let source = pad_string(&self.source, chunk as usize);
let name = format!("Querying {}", name.green().bold());
self.bar
.set_message(format!("{} on {}", name, source.yellow()));
}
}
impl Handler for FetchHandler {
fn handle(
&mut self,
_state: &mut crate::state::State,
action: &crate::action::Action,
) -> color_eyre::Result<bool> {
match action {
&Action::Stop { id, .. } if *id == self.id => {
self.bar.finish_and_clear();
Ok(false)
}
_ => Ok(true),
}
}
fn on_resize(&mut self, state: &mut crate::state::State) -> color_eyre::Result<()> {
self.format(state.term_width);
Ok(())
}
}

36
src/handlers/global.rs Normal file
View File

@@ -0,0 +1,36 @@
use indicatif::ProgressBar;
use crate::{
action::{Action, StartFields},
handlers::{Handler, fetch::FetchHandler},
};
pub struct GlobalHandler;
impl Handler for GlobalHandler {
fn handle(
&mut self,
state: &mut crate::state::State,
action: &crate::action::Action,
) -> color_eyre::Result<bool> {
match action {
Action::Start {
start_type: StartFields::QueryPathInfo { path, source },
id,
..
} => {
let bar = state.add_pb(ProgressBar::new_spinner());
state.plug(FetchHandler::new(
**id,
state.term_width,
path.clone(),
source.clone(),
bar,
));
// Handle global start action
Ok(true)
}
_ => Ok(true),
}
}
}

16
src/handlers/mod.rs Normal file
View File

@@ -0,0 +1,16 @@
use crate::action::Action;
pub mod download;
pub mod fetch;
pub mod global;
pub trait Handler {
fn handle(
&mut self,
state: &mut crate::state::State,
action: &Action,
) -> color_eyre::Result<bool>;
fn on_resize(&mut self, _state: &mut crate::state::State) -> color_eyre::Result<()> {
Ok(())
}
}

View File

@@ -1,7 +0,0 @@
pub mod action;
pub mod action_raw;
pub mod download_pb;
pub mod estimator;
pub mod multibar;
pub mod nix_path;
pub mod state_manager;

View File

@@ -1,290 +1,198 @@
use std::cell::LazyCell;
use std::sync::LazyLock;
use std::thread::sleep;
use std::time::Duration;
use console::{Term, style};
use indicatif::{MultiProgress, ProgressBar, ProgressStyle, TermLike};
use console::Term;
use crate::action::StartFields;
use crate::multibar::{BarSegment, MultiBar};
use crate::state_manager::{BuildEnumState, BuildState, State, StateManager};
static LAZY: LazyLock<i32> = LazyLock::new(|| {
println!("initializing");
92
});
use crate::state::State;
pub mod action;
pub mod action_raw;
pub mod download_pb;
pub mod estimator;
pub mod handlers;
pub mod multibar;
pub mod nix_path;
pub mod state;
pub mod state_manager;
#[derive(Debug, Clone)]
struct TextTerm;
impl TermLike for TextTerm {
fn width(&self) -> u16 {
50
}
fn move_cursor_up(&self, n: usize) -> std::io::Result<()> {
println!("Move cursor up by {n}");
Ok(())
}
fn move_cursor_down(&self, n: usize) -> std::io::Result<()> {
println!("Move cursor down by {n}");
Ok(())
}
fn move_cursor_right(&self, n: usize) -> std::io::Result<()> {
println!("Move cursor right by {n}");
Ok(())
}
fn move_cursor_left(&self, n: usize) -> std::io::Result<()> {
println!("Move cursor left by {n}");
Ok(())
}
fn write_line(&self, s: &str) -> std::io::Result<()> {
println!("write_line: {s:?}");
Ok(())
}
fn write_str(&self, s: &str) -> std::io::Result<()> {
println!("write_str: {s:?}");
Ok(())
}
fn clear_line(&self) -> std::io::Result<()> {
println!("Clear line");
Ok(())
}
fn flush(&self) -> std::io::Result<()> {
println!("Flush");
Ok(())
pub 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
}
}
fn main() -> Result<(), color_eyre::Report> {
color_eyre::install().unwrap();
// println!("Lazy value: {}", *LAZY);
// 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();
// 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 mut state = State::new(Term::stderr().size().1);
// let lines = lines.lines().take(0).collect::<Vec<_>>().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();
// 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(
// ProgressStyle::default_spinner()
// .template("[{elapsed_precise:>6}] {msg}")
// .unwrap(),
// );
// pb1.set_message("Processing line2342s\n|\n|\n>");
// let pb2 = progress.add(ProgressBar::new(lines.lines().count() as u64));
// pb2.set_style(
// ProgressStyle::default_spinner()
// .template("[{elapsed_precise:>6}] {msg}")
// .unwrap(),
// );
// pb2.set_message("Processing line2342s\n|\n|\n>");
// sleep(Duration::from_secs(1));
// let mut map = HashMap::new();
// 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,
// };
for line in lines.lines() {
// 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.println(format!("MSG (level {level}): {msg}"))?;
}
action::Action::Start {
start_type,
id,
level: _,
parent,
text: _,
} => {
// state.progress.println(format!("START {start_type:?}"));
if let Some(parent) = state.manager.get(*parent) {
let mut child = parent.clone();
child.merge(&mut state, start_type);
child.tick(&mut state);
state.manager.insert_parent(*id, child);
};
// match start_type {
// StartFields::Substitute { source, target } => {
// state
// .manager
// .insert_parent(*id, BuildState::new(Some(source.to_string()), None));
//
// let build_state = state.manager.get_mut(*id).unwrap();
// build_state.state = BuildEnumState::Substituting;
// }
// StartFields::CopyPath {
// path,
// origin,
// destination,
// } => {
// state.manager.add_child(*id, *parent);
// }
// StartFields::QueryPathInfo { path, source } => {
// state.progress.println(format!(
// "START QueryPathInfo (id: {}, parent: {}): path={}",
// id, parent, path
// ))?;
// }
// StartFields::FileTransfer { target } => {
// // state.progress.println(format!(
// // "START FileTransfer (id: {}, parent: {}): target={}",
// // id, parent, target
// // ))?;
// if let Some(parent) = state.manager.get(*parent) {
// state
// .manager
// .insert_parent(*id, BuildState::new(parent.path.clone(), None));
// let build_state = state.manager.get_mut(*id).unwrap();
// build_state.state = BuildEnumState::Downloading;
// };
//
// // state.manager.add_child(*id, *parent);
// // Add child ID mapping to parent
// }
// _ => {}
// };
}
action::Action::Stop { id } => {
state.manager.remove(*id);
// Stop will only return Some when the last reference is stopped
// if let Some(build_state) = state.manager.stop(*id) {
// if let Some(pb) = &build_state.progress_bar {
// pb.finish_and_clear();
// state.progress.remove(pb);
// }
// state.progress.println(format!(
// "Completed: {}",
// build_state.path.as_deref().unwrap_or("unknown")
// ))?;
// }
}
action::Action::Result { id, fields } => match fields {
action::ResultFields::FetchStatus(status) => {
state.println(format!(
"RESULT FetchStatus (id: {}): status={}",
id, status
))?;
}
action::ResultFields::Progress {
done,
expected,
running,
failed,
} => {
if expected == 0 {
continue;
};
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)
// && expected > 0
// {
// let percentage = if expected == 0 {
// 0
// } else {
// (done * 100 / expected) as u64
// };
// match &build_state.progress_bar {
// Some(pb) => {
// if percentage > pb.position() {
// pb.set_position(percentage);
// };
// }
// None => {
// state.progress.println(format!(
// "Creating progress bar for id {} (done: {}, expected: {})",
// id, done, expected
// ))?;
// let n = match build_state.state {
// BuildEnumState::Downloading => "Downloading",
// BuildEnumState::Substituting => "Substituting",
// _ => "Processing",
// };
// let name =
// nix_path::extract_full_name(build_state.path.as_ref().unwrap());
// let pb = state.progress.add(
// ProgressBar::new(100)
// .with_style(
// ProgressStyle::default_bar()
// .template("{msg} [{bar:40.cyan/blue}] {pos:>3}%")
// .unwrap(),
// )
// .with_message(format!(
// "{n} {}",
// name.as_deref().unwrap_or("unknown")
// )),
// );
// pb.set_position(percentage);
// build_state.progress_bar = Some(pb);
// }
// };
// } else {
// // state.progress.println(format!(
// // "RESULT Progress (id: {}): done={}, expected={}, running={}, failed={}",
// // id, done, expected, running, failed
// // ))?;
// };
}
_ => {}
},
}
state.handle(&action)?;
// match action {
// action::Action::Msg { level, msg } => {
// state.println(format!("MSG (level {level}): {msg}"))?;
// }
// action::Action::Start {
// start_type,
// id,
// level: _,
// parent,
// text: _,
// } => {
// // state.progress.println(format!("START {start_type:?}"));
// if let Some(parent) = state.manager.get(*parent) {
// let mut child = parent.clone();
// child.merge(&mut state, start_type);
// child.tick(&mut state);
// state.manager.insert_parent(*id, child);
// };
// // match start_type {
// // StartFields::Substitute { source, target } => {
// // state
// // .manager
// // .insert_parent(*id, BuildState::new(Some(source.to_string()), None));
// //
// // let build_state = state.manager.get_mut(*id).unwrap();
// // build_state.state = BuildEnumState::Substituting;
// // }
// // StartFields::CopyPath {
// // path,
// // origin,
// // destination,
// // } => {
// // state.manager.add_child(*id, *parent);
// // }
// // StartFields::QueryPathInfo { path, source } => {
// // state.progress.println(format!(
// // "START QueryPathInfo (id: {}, parent: {}): path={}",
// // id, parent, path
// // ))?;
// // }
// // StartFields::FileTransfer { target } => {
// // // state.progress.println(format!(
// // // "START FileTransfer (id: {}, parent: {}): target={}",
// // // id, parent, target
// // // ))?;
// // if let Some(parent) = state.manager.get(*parent) {
// // state
// // .manager
// // .insert_parent(*id, BuildState::new(parent.path.clone(), None));
// // let build_state = state.manager.get_mut(*id).unwrap();
// // build_state.state = BuildEnumState::Downloading;
// // };
// //
// // // state.manager.add_child(*id, *parent);
// // // Add child ID mapping to parent
// // }
// // _ => {}
// // };
// }
// action::Action::Stop { id } => {
// state.manager.remove(*id);
// // Stop will only return Some when the last reference is stopped
// // if let Some(build_state) = state.manager.stop(*id) {
// // if let Some(pb) = &build_state.progress_bar {
// // pb.finish_and_clear();
// // state.progress.remove(pb);
// // }
// // state.progress.println(format!(
// // "Completed: {}",
// // build_state.path.as_deref().unwrap_or("unknown")
// // ))?;
// // }
// }
// action::Action::Result { id, fields } => match fields {
// action::ResultFields::FetchStatus(status) => {
// state.println(format!(
// "RESULT FetchStatus (id: {}): status={}",
// id, status
// ))?;
// }
// action::ResultFields::Progress {
// done,
// expected,
// running,
// failed,
// } => {
// if expected == 0 {
// continue;
// };
// 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)
// // && expected > 0
// // {
// // let percentage = if expected == 0 {
// // 0
// // } else {
// // (done * 100 / expected) as u64
// // };
// // match &build_state.progress_bar {
// // Some(pb) => {
// // if percentage > pb.position() {
// // pb.set_position(percentage);
// // };
// // }
// // None => {
// // state.progress.println(format!(
// // "Creating progress bar for id {} (done: {}, expected: {})",
// // id, done, expected
// // ))?;
// // let n = match build_state.state {
// // BuildEnumState::Downloading => "Downloading",
// // BuildEnumState::Substituting => "Substituting",
// // _ => "Processing",
// // };
// // let name =
// // nix_path::extract_full_name(build_state.path.as_ref().unwrap());
// // let pb = state.progress.add(
// // ProgressBar::new(100)
// // .with_style(
// // ProgressStyle::default_bar()
// // .template("{msg} [{bar:40.cyan/blue}] {pos:>3}%")
// // .unwrap(),
// // )
// // .with_message(format!(
// // "{n} {}",
// // name.as_deref().unwrap_or("unknown")
// // )),
// // );
// // pb.set_position(percentage);
// // build_state.progress_bar = Some(pb);
// // }
// // };
// // } else {
// // // state.progress.println(format!(
// // // "RESULT Progress (id: {}): done={}, expected={}, running={}, failed={}",
// // // id, done, expected, running, failed
// // // ))?;
// // };
// }
// _ => {}
// },
// }
}
Ok(())

View File

@@ -1,6 +1,6 @@
const NIX_STORE_PREFIX: &str = "/nix/store/";
const NIX_HASH_LENGTH: usize = 32;
pub fn extract_package_name(path: &str) -> Option<String> {
pub fn extract_package_name(path: &str) -> Option<Vec<&str>> {
let without_prefix = path.strip_prefix(NIX_STORE_PREFIX)?;
if without_prefix.len() <= NIX_HASH_LENGTH + 1 {
@@ -24,7 +24,10 @@ pub fn extract_package_name(path: &str) -> Option<String> {
return None;
}
Some(name_parts.join("-"))
Some(name_parts)
}
pub fn extract_package_name_string(path: &str) -> Option<String> {
extract_package_name(path).map(|f| f.join("-"))
}
pub fn extract_full_name(path: &str) -> Option<&str> {

90
src/state.rs Normal file
View File

@@ -0,0 +1,90 @@
use std::{io, marker::PhantomData, rc::Rc};
use console::style;
use indicatif::{MultiProgress, ProgressBar, ProgressFinish, ProgressStyle};
use crate::handlers::{Handler, download::SubstituteHandler, global::GlobalHandler};
pub struct State {
pub multi_progress: Rc<MultiProgress>,
pub handlers: Vec<Box<dyn Handler>>,
pub term_width: u16,
pub separator: Option<ProgressBar>,
}
impl State {
pub fn new(term_width: u16) -> Self {
let mut state = Self {
multi_progress: Rc::new(MultiProgress::new()),
handlers: Vec::new(),
term_width,
separator: None,
};
state.plug(GlobalHandler);
state.plug(SubstituteHandler);
state
}
pub fn handle(&mut self, action: &crate::action::Action) -> color_eyre::Result<()> {
let mut prev_handlers = std::mem::take(&mut self.handlers);
// Check if terminal was resized
let (_, term_width) = console::Term::stderr().size();
if term_width != self.term_width {
self.term_width = term_width;
for handler in &mut prev_handlers {
handler.on_resize(self)?;
}
}
if let Some(separator) = &self.separator {
separator.tick();
}
// Applies handles
let mut retain_result = Ok(());
prev_handlers.retain_mut(|h| match h.handle(self, action) {
Ok(x) => x,
Err(err) => {
retain_result = Err(err);
false
}
});
let mut new_handlers = std::mem::replace(&mut self.handlers, prev_handlers);
self.handlers.append(&mut new_handlers);
retain_result
}
pub fn plug<H: Handler + 'static>(&mut self, handler: H) {
self.handlers.push(Box::new(handler))
}
pub fn add_pb(&mut self, pb: ProgressBar) -> ProgressBar {
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)
}
}

View File

@@ -103,7 +103,8 @@ impl<'a> BuildState<'a> {
BuildEnumState::Substitute { source, target }
}
(BuildEnumState::Substitute { .. }, StartFields::CopyPath { path, .. }) => {
let name = nix_path::extract_package_name(&path).unwrap_or("unknown".to_string());
let name =
nix_path::extract_package_name_string(&path).unwrap_or("unknown".to_string());
let bar = state.add_pb(ProgressBar::new(100));
let bar = DownloadBar::new(bar, name, state.width);
BuildEnumState::SubstituteCopy { path, bar }