meow meow
This commit is contained in:
4
src/lib.rs
Normal file
4
src/lib.rs
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
pub mod action;
|
||||||
|
pub mod action_raw;
|
||||||
|
pub mod nix_path;
|
||||||
|
pub mod state_manager;
|
||||||
206
src/main.rs
206
src/main.rs
@@ -1,35 +1,29 @@
|
|||||||
use std::{collections::HashMap, thread::sleep, time::Duration};
|
use std::thread::sleep;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
|
use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
|
||||||
|
|
||||||
use crate::action::StartFields;
|
use crate::action::StartFields;
|
||||||
|
use crate::state_manager::{BuildEnumState, BuildState, StateManager};
|
||||||
|
|
||||||
pub mod action;
|
pub mod action;
|
||||||
pub mod action_raw;
|
pub mod action_raw;
|
||||||
|
pub mod nix_path;
|
||||||
enum BuildEnumState {
|
pub mod state_manager;
|
||||||
Query,
|
|
||||||
Downloading,
|
|
||||||
}
|
|
||||||
struct BuildState {
|
|
||||||
path: Option<String>,
|
|
||||||
progress_bar: ProgressBar,
|
|
||||||
state: BuildEnumState,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct State {
|
struct State {
|
||||||
progress: MultiProgress,
|
progress: MultiProgress,
|
||||||
bars: HashMap<u64, BuildState>,
|
manager: StateManager,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<(), color_eyre::Report> {
|
fn main() -> Result<(), color_eyre::Report> {
|
||||||
color_eyre::install().unwrap();
|
color_eyre::install().unwrap();
|
||||||
let lines = std::fs::read_to_string("log2")?;
|
let lines = std::fs::read_to_string("build.log")?;
|
||||||
// let lines = lines.lines().take(1000).collect::<Vec<_>>().join("\n");
|
// let lines = lines.lines().take(1000).collect::<Vec<_>>().join("\n");
|
||||||
|
|
||||||
let mut state = State {
|
let mut state = State {
|
||||||
progress: MultiProgress::new(),
|
progress: MultiProgress::new(),
|
||||||
bars: HashMap::new(),
|
manager: StateManager::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// let progress = MultiProgress::new();
|
// let progress = MultiProgress::new();
|
||||||
@@ -58,7 +52,7 @@ fn main() -> Result<(), color_eyre::Report> {
|
|||||||
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(50));
|
sleep(Duration::from_millis(10));
|
||||||
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 } => {
|
||||||
@@ -69,81 +63,123 @@ fn main() -> Result<(), color_eyre::Report> {
|
|||||||
action::Action::Start {
|
action::Action::Start {
|
||||||
start_type,
|
start_type,
|
||||||
id,
|
id,
|
||||||
level,
|
level: _,
|
||||||
parent,
|
parent,
|
||||||
text,
|
text: _,
|
||||||
} => match start_type {
|
} => {
|
||||||
StartFields::QueryPathInfo { path, source } => {
|
state.progress.println(format!("START {start_type:?}"));
|
||||||
state.bars.insert(
|
match start_type {
|
||||||
*id,
|
StartFields::Substitute { source, target } => {
|
||||||
BuildState {
|
state
|
||||||
path: Some(path.to_string()),
|
.manager
|
||||||
progress_bar: state.progress.add(ProgressBar::new(100)),
|
.insert_parent(*id, BuildState::new(Some(source.to_string()), None));
|
||||||
state: BuildEnumState::Query,
|
|
||||||
},
|
let build_state = state.manager.get_mut(*id).unwrap();
|
||||||
);
|
build_state.state = BuildEnumState::Substituting;
|
||||||
}
|
|
||||||
StartFields::FileTransfer { target } => {
|
|
||||||
state.progress.println(format!(
|
|
||||||
"START FileTransfer (id: {}, parent: {}): target={}",
|
|
||||||
id, parent, target
|
|
||||||
))?;
|
|
||||||
if let Some(bar) = state.bars.get_mut(&parent) {
|
|
||||||
bar.state = BuildEnumState::Downloading;
|
|
||||||
bar.progress_bar.set_style(
|
|
||||||
ProgressStyle::default_bar()
|
|
||||||
.template("{msg} [{bar:40.cyan/blue}] {pos:>3}%")
|
|
||||||
.unwrap(),
|
|
||||||
);
|
|
||||||
bar.progress_bar.set_message(format!(
|
|
||||||
"Downloading {}",
|
|
||||||
bar.path.as_deref().unwrap_or("unknown")
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
// let bar = progress.add(ProgressBar::new(100));
|
StartFields::CopyPath {
|
||||||
// bar // bar.set_message(format!("meow {id} "));
|
path,
|
||||||
// map.insert(id, bar);
|
origin,
|
||||||
|
destination,
|
||||||
|
} => {
|
||||||
|
state.manager.add_child(*id, *parent);
|
||||||
|
}
|
||||||
|
// StartFields::QueryPathInfo { path, source: _ } => {
|
||||||
|
// state
|
||||||
|
// .manager
|
||||||
|
// .insert_parent(*id, BuildState::new(Some(path.to_string()), None));
|
||||||
|
// }
|
||||||
|
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 } => {
|
||||||
|
// 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::Progress {
|
||||||
|
done,
|
||||||
|
expected,
|
||||||
|
running,
|
||||||
|
failed,
|
||||||
|
} => {
|
||||||
|
// 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
|
||||||
|
))?;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
action::Action::Stop { id } => {
|
|
||||||
// if let Some(bar) = map.get(&id) {
|
|
||||||
// bar.finish();
|
|
||||||
// // bar.finish_with_message(format!("Transfer stopped (id: {})", id));
|
|
||||||
// progress.remove(bar);
|
|
||||||
// map.remove(&id);
|
|
||||||
// }
|
|
||||||
// progress.println(format!("STOP (id: {})", id))?;
|
|
||||||
}
|
|
||||||
action::Action::Result { id, fields } => {
|
|
||||||
// progress.println(format!("RESULT (id: {}): {:?}", id, fields))?;
|
|
||||||
match fields {
|
|
||||||
// action::ResultFields::Progress {
|
|
||||||
// done,
|
|
||||||
// expected,
|
|
||||||
// running,
|
|
||||||
// failed,
|
|
||||||
// } => {
|
|
||||||
// // sleep(Duration::from_millis(1));
|
|
||||||
// // if let Some(bar) = map.get(&id) {
|
|
||||||
// // let percentage = if expected == 0 {
|
|
||||||
// // 0
|
|
||||||
// // } else {
|
|
||||||
// // (done * 100 / expected) as u64
|
|
||||||
// // };
|
|
||||||
// // bar.set_position(percentage);
|
|
||||||
// // if done >= expected {
|
|
||||||
// // bar.finish_with_message(format!("Completed transfer (id: {})", id));
|
|
||||||
// // map.remove(&id);
|
|
||||||
// // }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
println!("{action:#?}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
38
src/nix_path.rs
Normal file
38
src/nix_path.rs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
const NIX_STORE_PREFIX: &str = "/nix/store/";
|
||||||
|
const NIX_HASH_LENGTH: usize = 32;
|
||||||
|
pub fn extract_package_name(path: &str) -> Option<String> {
|
||||||
|
let without_prefix = path.strip_prefix(NIX_STORE_PREFIX)?;
|
||||||
|
|
||||||
|
if without_prefix.len() <= NIX_HASH_LENGTH + 1 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let after_hash = &without_prefix[NIX_HASH_LENGTH + 1..];
|
||||||
|
let parts: Vec<&str> = after_hash.split('-').collect();
|
||||||
|
|
||||||
|
if parts.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let name_parts: Vec<&str> = parts
|
||||||
|
.iter()
|
||||||
|
.take_while(|part| !part.chars().next().map_or(false, |c| c.is_ascii_digit()))
|
||||||
|
.copied()
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if name_parts.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(name_parts.join("-"))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn extract_full_name(path: &str) -> Option<&str> {
|
||||||
|
let without_prefix = path.strip_prefix(NIX_STORE_PREFIX)?;
|
||||||
|
|
||||||
|
if without_prefix.len() <= NIX_HASH_LENGTH + 1 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(&without_prefix[NIX_HASH_LENGTH + 1..])
|
||||||
|
}
|
||||||
107
src/state_manager.rs
Normal file
107
src/state_manager.rs
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
use indicatif::ProgressBar;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum BuildEnumState {
|
||||||
|
Query,
|
||||||
|
Downloading,
|
||||||
|
Substituting,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct BuildState {
|
||||||
|
pub path: Option<String>,
|
||||||
|
pub progress_bar: Option<ProgressBar>,
|
||||||
|
pub state: BuildEnumState,
|
||||||
|
ref_count: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BuildState {
|
||||||
|
pub fn new(path: Option<String>, progress_bar: Option<ProgressBar>) -> Self {
|
||||||
|
Self {
|
||||||
|
path,
|
||||||
|
progress_bar,
|
||||||
|
state: BuildEnumState::Query,
|
||||||
|
ref_count: 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct StateManager {
|
||||||
|
states: HashMap<u64, BuildState>,
|
||||||
|
id_to_canonical: HashMap<u64, u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StateManager {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
states: HashMap::new(),
|
||||||
|
id_to_canonical: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_parent(&mut self, id: u64, state: BuildState) {
|
||||||
|
self.id_to_canonical.insert(id, id); // parent maps to itself
|
||||||
|
self.states.insert(id, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_child(&mut self, child_id: u64, parent_id: u64) {
|
||||||
|
if let Some(&canonical_id) = self.id_to_canonical.get(&parent_id) {
|
||||||
|
self.id_to_canonical.insert(child_id, canonical_id);
|
||||||
|
|
||||||
|
if let Some(state) = self.states.get_mut(&canonical_id) {
|
||||||
|
state.ref_count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self, id: u64) -> Option<&BuildState> {
|
||||||
|
self.id_to_canonical
|
||||||
|
.get(&id)
|
||||||
|
.and_then(|canonical_id| self.states.get(canonical_id))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_mut(&mut self, id: u64) -> Option<&mut BuildState> {
|
||||||
|
self.id_to_canonical
|
||||||
|
.get(&id)
|
||||||
|
.copied()
|
||||||
|
.and_then(|canonical_id| self.states.get_mut(&canonical_id))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stop(&mut self, id: u64) -> Option<BuildState> {
|
||||||
|
let canonical_id = self.id_to_canonical.get(&id).copied()?;
|
||||||
|
|
||||||
|
self.id_to_canonical.remove(&id);
|
||||||
|
|
||||||
|
if let Some(state) = self.states.get_mut(&canonical_id) {
|
||||||
|
state.ref_count -= 1;
|
||||||
|
|
||||||
|
if state.ref_count == 0 {
|
||||||
|
return self.states.remove(&canonical_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_canonical_id(&self, id: u64) -> Option<u64> {
|
||||||
|
self.id_to_canonical.get(&id).copied()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn contains(&self, id: u64) -> bool {
|
||||||
|
self.id_to_canonical.contains_key(&id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.states.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.states.is_empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for StateManager {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user