rewrite most of the handler code :3
This commit is contained in:
@@ -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
238
src/handlers/download.rs
Normal 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
63
src/handlers/fetch.rs
Normal 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
36
src/handlers/global.rs
Normal 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
16
src/handlers/mod.rs
Normal 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(())
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
430
src/main.rs
430
src/main.rs
@@ -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(())
|
||||
|
||||
@@ -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
90
src/state.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
@@ -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 }
|
||||
|
||||
Reference in New Issue
Block a user